Panda3D
 All Classes Functions Variables Enumerations
texture.cxx
00001 // Filename: texture.cxx
00002 // Created by:  mike (09Jan97)
00003 // Updated by: fperazzi, PandaSE(29Apr10) (added TT_2d_texture_array)
00004 //
00005 ////////////////////////////////////////////////////////////////////
00006 //
00007 // PANDA 3D SOFTWARE
00008 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00009 //
00010 // All use of this software is subject to the terms of the revised BSD
00011 // license.  You should have received a copy of this license along
00012 // with this source code in a file named "LICENSE."
00013 //
00014 ////////////////////////////////////////////////////////////////////
00015 
00016 #include "pandabase.h"
00017 #include "texture.h"
00018 #include "config_gobj.h"
00019 #include "config_util.h"
00020 #include "texturePool.h"
00021 #include "textureContext.h"
00022 #include "bamCache.h"
00023 #include "bamCacheRecord.h"
00024 #include "datagram.h"
00025 #include "datagramIterator.h"
00026 #include "bamReader.h"
00027 #include "bamWriter.h"
00028 #include "string_utils.h"
00029 #include "preparedGraphicsObjects.h"
00030 #include "pnmImage.h"
00031 #include "virtualFileSystem.h"
00032 #include "datagramInputFile.h"
00033 #include "datagramOutputFile.h"
00034 #include "bam.h"
00035 #include "zStream.h"
00036 #include "indent.h"
00037 #include "cmath.h"
00038 #include "pStatTimer.h"
00039 #include "pbitops.h"
00040 #include "streamReader.h"
00041 #include "texturePeeker.h"
00042 
00043 #ifdef HAVE_SQUISH
00044 #include <squish.h>
00045 #endif  // HAVE_SQUISH
00046 
00047 #include <stddef.h>
00048 
00049 ConfigVariableEnum<Texture::QualityLevel> texture_quality_level
00050 ("texture-quality-level", Texture::QL_normal,
00051  PRC_DESC("This specifies a global quality level for all textures.  You "
00052           "may specify either fastest, normal, or best.  This actually "
00053           "affects the meaning of Texture::set_quality_level(QL_default), "
00054           "so it may be overridden on a per-texture basis.  This generally "
00055           "only has an effect when using the tinydisplay software renderer; "
00056           "it has little or no effect on normal, hardware-accelerated "
00057           "renderers.  See Texture::set_quality_level()."));
00058 
00059 ConfigVariableEnum<Texture::FilterType> texture_minfilter
00060 ("texture-minfilter", Texture::FT_linear,
00061  PRC_DESC("This specifies the default minfilter that is applied to a texture "
00062           "in the absence of a specific minfilter setting.  Normally this "
00063           "is either 'linear' to disable mipmapping by default, or "
00064           "'mipmap', to enable trilinear mipmapping by default.  This "
00065           "does not apply to depth textures.  Note if this variable is "
00066           "changed at runtime, you may need to reload textures explicitly "
00067           "in order to change their visible properties."));
00068 
00069 ConfigVariableEnum<Texture::FilterType> texture_magfilter
00070 ("texture-magfilter", Texture::FT_linear,
00071  PRC_DESC("This specifies the default magfilter that is applied to a texture "
00072           "in the absence of a specific magfilter setting.  Normally this "
00073           "is 'linear' (since mipmapping does not apply to magfilters).  This "
00074           "does not apply to depth textures.  Note if this variable is "
00075           "changed at runtime, you may need to reload textures explicitly "
00076           "in order to change their visible properties."));
00077 
00078 ConfigVariableInt texture_anisotropic_degree
00079 ("texture-anisotropic-degree", 1,
00080  PRC_DESC("This specifies the default anisotropic degree that is applied "
00081           "to a texture in the absence of a particular anisotropic degree "
00082           "setting (that is, a texture for which the anisotropic degree "
00083           "is 0, meaning the default setting).  It should be 1 to disable "
00084           "anisotropic filtering, or a higher number to enable it.  "
00085           "Note if this variable is "
00086           "changed at runtime, you may need to reload textures explicitly "
00087           "in order to change their visible properties."));
00088 
00089 PStatCollector Texture::_texture_read_pcollector("*:Texture:Read");
00090 TypeHandle Texture::_type_handle;
00091 TypeHandle Texture::CData::_type_handle;
00092 AutoTextureScale Texture::_textures_power_2 = ATS_unspecified;
00093 
00094 // Stuff to read and write DDS files.
00095 
00096 //  little-endian, of course
00097 #define DDS_MAGIC 0x20534444
00098 
00099 
00100 //  DDS_header.dwFlags
00101 #define DDSD_CAPS                   0x00000001
00102 #define DDSD_HEIGHT                 0x00000002
00103 #define DDSD_WIDTH                  0x00000004
00104 #define DDSD_PITCH                  0x00000008
00105 #define DDSD_PIXELFORMAT            0x00001000
00106 #define DDSD_MIPMAPCOUNT            0x00020000
00107 #define DDSD_LINEARSIZE             0x00080000
00108 #define DDSD_DEPTH                  0x00800000
00109 
00110 //  DDS_header.sPixelFormat.dwFlags
00111 #define DDPF_ALPHAPIXELS            0x00000001
00112 #define DDPF_FOURCC                 0x00000004
00113 #define DDPF_INDEXED                0x00000020
00114 #define DDPF_RGB                    0x00000040
00115 
00116 //  DDS_header.sCaps.dwCaps1
00117 #define DDSCAPS_COMPLEX             0x00000008
00118 #define DDSCAPS_TEXTURE             0x00001000
00119 #define DDSCAPS_MIPMAP              0x00400000
00120 
00121 //  DDS_header.sCaps.dwCaps2
00122 #define DDSCAPS2_CUBEMAP            0x00000200
00123 #define DDSCAPS2_CUBEMAP_POSITIVEX  0x00000400
00124 #define DDSCAPS2_CUBEMAP_NEGATIVEX  0x00000800
00125 #define DDSCAPS2_CUBEMAP_POSITIVEY  0x00001000
00126 #define DDSCAPS2_CUBEMAP_NEGATIVEY  0x00002000
00127 #define DDSCAPS2_CUBEMAP_POSITIVEZ  0x00004000
00128 #define DDSCAPS2_CUBEMAP_NEGATIVEZ  0x00008000
00129 #define DDSCAPS2_VOLUME             0x00200000
00130 
00131 struct DDSPixelFormat {
00132   unsigned int pf_size;
00133   unsigned int pf_flags;
00134   unsigned int four_cc;
00135   unsigned int rgb_bitcount;
00136   unsigned int r_mask;
00137   unsigned int g_mask;
00138   unsigned int b_mask;
00139   unsigned int a_mask;
00140 };
00141 
00142 struct DDSCaps2 {
00143   unsigned int caps1;
00144   unsigned int caps2;
00145   unsigned int ddsx;
00146 };
00147 
00148 struct DDSHeader {
00149   unsigned int dds_magic;
00150   unsigned int dds_size;
00151   unsigned int dds_flags;
00152   unsigned int height;
00153   unsigned int width;
00154   unsigned int pitch;
00155   unsigned int depth;
00156   unsigned int num_levels;
00157 
00158   DDSPixelFormat pf;
00159   DDSCaps2 caps;
00160 };
00161 
00162 ////////////////////////////////////////////////////////////////////
00163 //     Function: Texture::Constructor
00164 //       Access: Published
00165 //  Description: Constructs an empty texture.  The default is to set
00166 //               up the texture as an empty 2-d texture; follow up
00167 //               with one of the variants of setup_texture() if this
00168 //               is not what you want.
00169 ////////////////////////////////////////////////////////////////////
00170 Texture::
00171 Texture(const string &name) :
00172   Namable(name),
00173   _lock(name),
00174   _cvar(_lock)
00175 {
00176   _reloading = false;
00177 
00178   CDWriter cdata(_cycler, true);
00179   do_set_format(cdata, F_rgb);
00180   do_set_component_type(cdata, T_unsigned_byte);
00181 }
00182 
00183 ////////////////////////////////////////////////////////////////////
00184 //     Function: Texture::Copy Constructor
00185 //       Access: Protected
00186 //  Description: Use Texture::make_copy() to make a duplicate copy of
00187 //               an existing Texture.
00188 ////////////////////////////////////////////////////////////////////
00189 Texture::
00190 Texture(const Texture &copy) :
00191   Namable(copy),
00192   _cycler(copy._cycler),
00193   _lock(copy.get_name()),
00194   _cvar(_lock)
00195 {
00196   _reloading = false;
00197 }
00198 
00199 ////////////////////////////////////////////////////////////////////
00200 //     Function: Texture::Copy Assignment Operator
00201 //       Access: Protected
00202 //  Description: Use Texture::make_copy() to make a duplicate copy of
00203 //               an existing Texture.
00204 ////////////////////////////////////////////////////////////////////
00205 void Texture::
00206 operator = (const Texture &copy) {
00207   Namable::operator = (copy);
00208   _cycler = copy._cycler;
00209 }
00210 
00211 ////////////////////////////////////////////////////////////////////
00212 //     Function: Texture::Destructor
00213 //       Access: Published, Virtual
00214 //  Description:
00215 ////////////////////////////////////////////////////////////////////
00216 Texture::
00217 ~Texture() {
00218   release_all();
00219   nassertv(!_reloading);
00220 }
00221 
00222 ////////////////////////////////////////////////////////////////////
00223 //     Function: Texture::generate_normalization_cube_map
00224 //       Access: Published
00225 //  Description: Generates a special cube map image in the texture
00226 //               that can be used to apply bump mapping effects: for
00227 //               each texel in the cube map that is indexed by the 3-d
00228 //               texture coordinates (x, y, z), the resulting value is
00229 //               the normalized vector (x, y, z) (compressed from
00230 //               -1..1 into 0..1).
00231 ////////////////////////////////////////////////////////////////////
00232 void Texture::
00233 generate_normalization_cube_map(int size) {
00234   CDWriter cdata(_cycler, true);
00235   do_setup_texture(cdata, TT_cube_map, size, size, 6, T_unsigned_byte, F_rgb);
00236   PTA_uchar image = do_make_ram_image(cdata);
00237   cdata->_keep_ram_image = true;
00238 
00239   ++(cdata->_image_modified);
00240   ++(cdata->_properties_modified);
00241 
00242   PN_stdfloat half_size = (PN_stdfloat)size * 0.5f;
00243   PN_stdfloat center = half_size - 0.5f;
00244 
00245   LMatrix4 scale
00246     (127.5f, 0.0f, 0.0f, 0.0f,
00247      0.0f, 127.5f, 0.0f, 0.0f,
00248      0.0f, 0.0f, 127.5f, 0.0f,
00249      127.5f, 127.5f, 127.5f, 1.0f);
00250 
00251   unsigned char *p = image;
00252   int xi, yi;
00253 
00254   // Page 0: positive X.
00255   for (yi = 0; yi < size; ++yi) {
00256     for (xi = 0; xi < size; ++xi) {
00257       LVector3 vec(half_size, center - yi, center - xi);
00258       vec.normalize();
00259       vec = scale.xform_point(vec);
00260 
00261       *p++ = (unsigned char)vec[2];
00262       *p++ = (unsigned char)vec[1];
00263       *p++ = (unsigned char)vec[0];
00264     }
00265   }
00266 
00267   // Page 1: negative X.
00268   for (yi = 0; yi < size; ++yi) {
00269     for (xi = 0; xi < size; ++xi) {
00270       LVector3 vec(-half_size, center - yi, xi - center);
00271       vec.normalize();
00272       vec = scale.xform_point(vec);
00273       *p++ = (unsigned char)vec[2];
00274       *p++ = (unsigned char)vec[1];
00275       *p++ = (unsigned char)vec[0];
00276     }
00277   }
00278 
00279   // Page 2: positive Y.
00280   for (yi = 0; yi < size; ++yi) {
00281     for (xi = 0; xi < size; ++xi) {
00282       LVector3 vec(xi - center, half_size, yi - center);
00283       vec.normalize();
00284       vec = scale.xform_point(vec);
00285       *p++ = (unsigned char)vec[2];
00286       *p++ = (unsigned char)vec[1];
00287       *p++ = (unsigned char)vec[0];
00288     }
00289   }
00290 
00291   // Page 3: negative Y.
00292   for (yi = 0; yi < size; ++yi) {
00293     for (xi = 0; xi < size; ++xi) {
00294       LVector3 vec(xi - center, -half_size, center - yi);
00295       vec.normalize();
00296       vec = scale.xform_point(vec);
00297       *p++ = (unsigned char)vec[2];
00298       *p++ = (unsigned char)vec[1];
00299       *p++ = (unsigned char)vec[0];
00300     }
00301   }
00302 
00303   // Page 4: positive Z.
00304   for (yi = 0; yi < size; ++yi) {
00305     for (xi = 0; xi < size; ++xi) {
00306       LVector3 vec(xi - center, center - yi, half_size);
00307       vec.normalize();
00308       vec = scale.xform_point(vec);
00309       *p++ = (unsigned char)vec[2];
00310       *p++ = (unsigned char)vec[1];
00311       *p++ = (unsigned char)vec[0];
00312     }
00313   }
00314 
00315   // Page 5: negative Z.
00316   for (yi = 0; yi < size; ++yi) {
00317     for (xi = 0; xi < size; ++xi) {
00318       LVector3 vec(center - xi, center - yi, -half_size);
00319       vec.normalize();
00320       vec = scale.xform_point(vec);
00321       *p++ = (unsigned char)vec[2];
00322       *p++ = (unsigned char)vec[1];
00323       *p++ = (unsigned char)vec[0];
00324     }
00325   }
00326 }
00327 
00328 ////////////////////////////////////////////////////////////////////
00329 //     Function: Texture::generate_alpha_scale_map
00330 //       Access: Published
00331 //  Description: Generates a special 256x1 1-d texture that can be
00332 //               used to apply an arbitrary alpha scale to objects by
00333 //               judicious use of texture matrix.  The texture is a
00334 //               gradient, with an alpha of 0 on the left (U = 0), and
00335 //               255 on the right (U = 1).
00336 ////////////////////////////////////////////////////////////////////
00337 void Texture::
00338 generate_alpha_scale_map() {
00339   CDWriter cdata(_cycler, true);
00340   do_setup_texture(cdata, TT_1d_texture, 256, 1, 1, T_unsigned_byte, F_alpha);
00341   cdata->_wrap_u = WM_clamp;
00342   cdata->_minfilter = FT_nearest;
00343   cdata->_magfilter = FT_nearest;
00344   cdata->_compression = CM_off;
00345 
00346   ++(cdata->_image_modified);
00347   ++(cdata->_properties_modified);
00348 
00349   PTA_uchar image = do_make_ram_image(cdata);
00350   cdata->_keep_ram_image = true;
00351 
00352   unsigned char *p = image;
00353   for (int xi = 0; xi < 256; ++xi) {
00354     *p++ = xi;
00355   }
00356 }
00357 
00358 ////////////////////////////////////////////////////////////////////
00359 //     Function: Texture::read
00360 //       Access: Published
00361 //  Description: Reads the named filename into the texture.
00362 ////////////////////////////////////////////////////////////////////
00363 bool Texture::
00364 read(const Filename &fullpath, const LoaderOptions &options) {
00365   CDWriter cdata(_cycler, true);
00366   do_clear(cdata);
00367   ++(cdata->_properties_modified);
00368   ++(cdata->_image_modified);
00369   return do_read(cdata, fullpath, Filename(), 0, 0, 0, 0, false, false,
00370                  options, NULL);
00371 }
00372 
00373 ////////////////////////////////////////////////////////////////////
00374 //     Function: Texture::read
00375 //       Access: Published
00376 //  Description: Combine a 3-component image with a grayscale image
00377 //               to get a 4-component image.
00378 //
00379 //               See the description of the full-parameter read()
00380 //               method for the meaning of the
00381 //               primary_file_num_channels and alpha_file_channel
00382 //               parameters.
00383 ////////////////////////////////////////////////////////////////////
00384 bool Texture::
00385 read(const Filename &fullpath, const Filename &alpha_fullpath,
00386      int primary_file_num_channels, int alpha_file_channel,
00387      const LoaderOptions &options) {
00388   CDWriter cdata(_cycler, true);
00389   do_clear(cdata);
00390   ++(cdata->_properties_modified);
00391   ++(cdata->_image_modified);
00392   return do_read(cdata, fullpath, alpha_fullpath, primary_file_num_channels,
00393                  alpha_file_channel, 0, 0, false, false,
00394                  options, NULL);
00395 }
00396 
00397 ////////////////////////////////////////////////////////////////////
00398 //     Function: Texture::read
00399 //       Access: Published
00400 //  Description: Reads a single file into a single page or mipmap
00401 //               level, or automatically reads a series of files into
00402 //               a series of pages and/or mipmap levels.
00403 //
00404 //               See the description of the full-parameter read()
00405 //               method for the meaning of the various parameters.
00406 ////////////////////////////////////////////////////////////////////
00407 bool Texture::
00408 read(const Filename &fullpath, int z, int n,
00409      bool read_pages, bool read_mipmaps,
00410      const LoaderOptions &options) {
00411   CDWriter cdata(_cycler, true);
00412   ++(cdata->_properties_modified);
00413   ++(cdata->_image_modified);
00414   return do_read(cdata, fullpath, Filename(), 0, 0, z, n, read_pages, read_mipmaps,
00415                  options, NULL);
00416 }
00417 
00418 ////////////////////////////////////////////////////////////////////
00419 //     Function: Texture::read
00420 //       Access: Published
00421 //  Description: Reads the texture from the indicated filename.  If
00422 //               primary_file_num_channels is not 0, it specifies the
00423 //               number of components to downgrade the image to if it
00424 //               is greater than this number.
00425 //
00426 //               If the filename has the extension .txo, this
00427 //               implicitly reads a texture object instead of a
00428 //               filename (which replaces all of the texture
00429 //               properties).  In this case, all the rest of the
00430 //               parameters are ignored, and the filename should not
00431 //               contain any hash marks; just the one named file will
00432 //               be read, since a single .txo file can contain all
00433 //               pages and mipmaps necessary to define a texture.
00434 //
00435 //               If alpha_fullpath is not empty, it specifies the name
00436 //               of a file from which to retrieve the alpha.  In this
00437 //               case, alpha_file_channel represents the numeric
00438 //               channel of this image file to use as the resulting
00439 //               texture's alpha channel; usually, this is 0 to
00440 //               indicate the grayscale combination of r, g, b; or it
00441 //               may be a one-based channel number, e.g. 1 for the red
00442 //               channel, 2 for the green channel, and so on.
00443 //
00444 //               If read pages is false, then z indicates the page
00445 //               number into which this image will be assigned.
00446 //               Normally this is 0 for the first (or only) page of
00447 //               the texture.  3-D textures have one page for each
00448 //               level of depth, and cube map textures always have six
00449 //               pages.
00450 //
00451 //               If read_pages is true, multiple images will be read
00452 //               at once, one for each page of a cube map or a 3-D
00453 //               texture.  In this case, the filename should contain a
00454 //               sequence of one or more hash marks ("#") which will
00455 //               be filled in with the z value of each page,
00456 //               zero-based.  In this case, the z parameter indicates
00457 //               the maximum z value that will be loaded, or 0 to load
00458 //               all filenames that exist.
00459 //
00460 //               If read_mipmaps is false, then n indicates the mipmap
00461 //               level to which this image will be assigned.  Normally
00462 //               this is 0 for the base texture image, but it is
00463 //               possible to load custom mipmap levels into the later
00464 //               images.  After the base texture image is loaded (thus
00465 //               defining the size of the texture), you can call
00466 //               get_expected_num_mipmap_levels() to determine the
00467 //               maximum sensible value for n.
00468 //
00469 //               If read_mipmaps is true, multiple images will be read
00470 //               as above, but this time the images represent the
00471 //               different mipmap levels of the texture image.  In
00472 //               this case, the n parameter indicates the maximum n
00473 //               value that will be loaded, or 0 to load all filenames
00474 //               that exist (up to the expected number of mipmap
00475 //               levels).
00476 //
00477 //               If both read_pages and read_mipmaps is true, then
00478 //               both sequences will be read; the filename should
00479 //               contain two sequences of hash marks, separated by
00480 //               some character such as a hyphen, underscore, or dot.
00481 //               The first hash mark sequence will be filled in with
00482 //               the mipmap level, while the second hash mark sequence
00483 //               will be the page index.
00484 //
00485 //               This method implicitly sets keep_ram_image to false.
00486 ////////////////////////////////////////////////////////////////////
00487 bool Texture::
00488 read(const Filename &fullpath, const Filename &alpha_fullpath,
00489      int primary_file_num_channels, int alpha_file_channel,
00490      int z, int n, bool read_pages, bool read_mipmaps,
00491      BamCacheRecord *record,
00492      const LoaderOptions &options) {
00493   CDWriter cdata(_cycler, true);
00494   ++(cdata->_properties_modified);
00495   ++(cdata->_image_modified);
00496   return do_read(cdata, fullpath, alpha_fullpath, primary_file_num_channels,
00497                  alpha_file_channel, z, n, read_pages, read_mipmaps,
00498                  options, record);
00499 }
00500 
00501 ////////////////////////////////////////////////////////////////////
00502 //     Function: Texture::estimate_texture_memory
00503 //       Access: Published
00504 //  Description: Estimates the amount of texture memory that will be
00505 //               consumed by loading this texture.  This returns a
00506 //               value that is not specific to any particular graphics
00507 //               card or driver; it tries to make a reasonable
00508 //               assumption about how a driver will load the texture.
00509 //               It does not account for texture compression or
00510 //               anything fancy.  This is mainly useful for debugging
00511 //               and reporting purposes.
00512 //
00513 //               Returns a value in bytes.
00514 ////////////////////////////////////////////////////////////////////
00515 size_t Texture::
00516 estimate_texture_memory() const {
00517   CDReader cdata(_cycler);
00518   size_t pixels = cdata->_x_size * cdata->_y_size;
00519 
00520   size_t bpp = 4;
00521   switch (cdata->_format) {
00522   case Texture::F_rgb332:
00523     bpp = 1;
00524     break;
00525 
00526   case Texture::F_alpha:
00527   case Texture::F_red:
00528   case Texture::F_green:
00529   case Texture::F_blue:
00530   case Texture::F_luminance:
00531   case Texture::F_luminance_alpha:
00532   case Texture::F_luminance_alphamask:
00533     bpp = 4;
00534     break;
00535 
00536   case Texture::F_rgba:
00537   case Texture::F_rgba4:
00538   case Texture::F_rgbm:
00539   case Texture::F_rgb:
00540   case Texture::F_rgb5:
00541   case Texture::F_rgba5:
00542     bpp = 4;
00543     break;
00544 
00545   case Texture::F_color_index:
00546   case Texture::F_rgb8:
00547   case Texture::F_rgba8:
00548     bpp = 4;
00549     break;
00550 
00551   case Texture::F_depth_stencil:
00552   case Texture::F_depth_component:
00553     bpp = 32;
00554     break;
00555 
00556   case Texture::F_rgba12:
00557   case Texture::F_rgb12:
00558     bpp = 6;
00559     break;
00560 
00561   case Texture::F_rgba16:
00562     bpp = 8;
00563     break;
00564   case Texture::F_rgba32:
00565     bpp = 16;
00566     break;
00567 
00568   default:
00569     break;
00570   }
00571 
00572   size_t bytes = pixels * bpp;
00573   if (uses_mipmaps()) {
00574     bytes = (bytes * 4) / 3;
00575   }
00576 
00577   return bytes;
00578 }
00579 
00580 ////////////////////////////////////////////////////////////////////
00581 //     Function: Texture::set_aux_data
00582 //       Access: Published
00583 //  Description: Records an arbitrary object in the Texture,
00584 //               associated with a specified key.  The object may
00585 //               later be retrieved by calling get_aux_data() with the
00586 //               same key.
00587 //
00588 //               These data objects are not recorded to a bam or txo
00589 //               file.
00590 ////////////////////////////////////////////////////////////////////
00591 void Texture::
00592 set_aux_data(const string &key, TypedReferenceCount *aux_data) {
00593   MutexHolder holder(_lock);
00594   _aux_data[key] = aux_data;
00595 }
00596 
00597 ////////////////////////////////////////////////////////////////////
00598 //     Function: Texture::clear_aux_data
00599 //       Access: Published
00600 //  Description: Removes a record previously recorded via
00601 //               set_aux_data().
00602 ////////////////////////////////////////////////////////////////////
00603 void Texture::
00604 clear_aux_data(const string &key) {
00605   MutexHolder holder(_lock);
00606   _aux_data.erase(key);
00607 }
00608 
00609 ////////////////////////////////////////////////////////////////////
00610 //     Function: Texture::get_aux_data
00611 //       Access: Published
00612 //  Description: Returns a record previously recorded via
00613 //               set_aux_data().  Returns NULL if there was no record
00614 //               associated with the indicated key.
00615 ////////////////////////////////////////////////////////////////////
00616 TypedReferenceCount *Texture::
00617 get_aux_data(const string &key) const {
00618   MutexHolder holder(_lock);
00619   AuxData::const_iterator di;
00620   di = _aux_data.find(key);
00621   if (di != _aux_data.end()) {
00622     return (*di).second;
00623   }
00624   return NULL;
00625 }
00626 
00627 ////////////////////////////////////////////////////////////////////
00628 //     Function: Texture::read_txo
00629 //       Access: Published
00630 //  Description: Reads the texture from a Panda texture object.  This
00631 //               defines the complete Texture specification, including
00632 //               the image data as well as all texture properties.
00633 //               This only works if the txo file contains a static
00634 //               Texture image, as opposed to a subclass of Texture
00635 //               such as a movie texture.
00636 //
00637 //               Pass a real filename if it is available, or empty
00638 //               string if it is not.
00639 ////////////////////////////////////////////////////////////////////
00640 bool Texture::
00641 read_txo(istream &in, const string &filename) {
00642   CDWriter cdata(_cycler, true);
00643   ++(cdata->_properties_modified);
00644   ++(cdata->_image_modified);
00645   return do_read_txo(cdata, in, filename);
00646 }
00647 
00648 ////////////////////////////////////////////////////////////////////
00649 //     Function: Texture::make_from_txo
00650 //       Access: Published, Static
00651 //  Description: Constructs a new Texture object from the txo file.
00652 //               This is similar to Texture::read_txo(), but it
00653 //               constructs and returns a new object, which allows it
00654 //               to return a subclass of Texture (for instance, a
00655 //               movie texture).
00656 //
00657 //               Pass a real filename if it is available, or empty
00658 //               string if it is not.
00659 ////////////////////////////////////////////////////////////////////
00660 PT(Texture) Texture::
00661 make_from_txo(istream &in, const string &filename) {
00662   DatagramInputFile din;
00663 
00664   if (!din.open(in, filename)) {
00665     gobj_cat.error()
00666       << "Could not read texture object: " << filename << "\n";
00667     return NULL;
00668   }
00669 
00670   string head;
00671   if (!din.read_header(head, _bam_header.size())) {
00672     gobj_cat.error()
00673       << filename << " is not a texture object file.\n";
00674     return NULL;
00675   }
00676 
00677   if (head != _bam_header) {
00678     gobj_cat.error()
00679       << filename << " is not a texture object file.\n";
00680     return NULL;
00681   }
00682 
00683   BamReader reader(&din);
00684   if (!reader.init()) {
00685     return NULL;
00686   }
00687 
00688   TypedWritable *object = reader.read_object();
00689 
00690   if (object != (TypedWritable *)NULL &&
00691       object->is_exact_type(BamCacheRecord::get_class_type())) {
00692     // Here's a special case: if the first object in the file is a
00693     // BamCacheRecord, it's really a cache data file and not a true
00694     // txo file; but skip over the cache data record and let the user
00695     // treat it like an ordinary txo file.
00696     object = reader.read_object();
00697   }
00698 
00699   if (object == (TypedWritable *)NULL) {
00700     gobj_cat.error()
00701       << "Texture object " << filename << " is empty.\n";
00702     return NULL;
00703 
00704   } else if (!object->is_of_type(Texture::get_class_type())) {
00705     gobj_cat.error()
00706       << "Texture object " << filename << " contains a "
00707       << object->get_type() << ", not a Texture.\n";
00708     return NULL;
00709   }
00710 
00711   PT(Texture) other = DCAST(Texture, object);
00712   if (!reader.resolve()) {
00713     gobj_cat.error()
00714       << "Unable to fully resolve texture object file.\n";
00715     return NULL;
00716   }
00717 
00718   return other;
00719 }
00720 
00721 ////////////////////////////////////////////////////////////////////
00722 //     Function: Texture::write_txo
00723 //       Access: Published
00724 //  Description: Writes the texture to a Panda texture object.  This
00725 //               defines the complete Texture specification, including
00726 //               the image data as well as all texture properties.
00727 //
00728 //               The filename is just for reference.
00729 ////////////////////////////////////////////////////////////////////
00730 bool Texture::
00731 write_txo(ostream &out, const string &filename) const {
00732   CDReader cdata(_cycler);
00733   return do_write_txo(cdata, out, filename);
00734 }
00735 
00736 ////////////////////////////////////////////////////////////////////
00737 //     Function: Texture::read_dds
00738 //       Access: Published
00739 //  Description: Reads the texture from a DDS file object.  This is a
00740 //               Microsoft-defined file format; it is similar in
00741 //               principle to a txo object, in that it is designed to
00742 //               contain the texture image in a form as similar as
00743 //               possible to its runtime image, and it can contain
00744 //               mipmaps, pre-compressed textures, and so on.
00745 //
00746 //               As with read_txo, the filename is just for reference.
00747 ////////////////////////////////////////////////////////////////////
00748 bool Texture::
00749 read_dds(istream &in, const string &filename, bool header_only) {
00750   CDWriter cdata(_cycler, true);
00751   ++(cdata->_properties_modified);
00752   ++(cdata->_image_modified);
00753   return do_read_dds(cdata, in, filename, header_only);
00754 }
00755 
00756 ////////////////////////////////////////////////////////////////////
00757 //     Function: Texture::load_related
00758 //       Access: Published
00759 //  Description: Loads a texture whose filename is derived by
00760 //               concatenating a suffix to the filename of this
00761 //               texture.  May return NULL, for example, if this
00762 //               texture doesn't have a filename.
00763 ////////////////////////////////////////////////////////////////////
00764 Texture *Texture::
00765 load_related(const InternalName *suffix) const {
00766   MutexHolder holder(_lock);
00767   CDReader cdata(_cycler);
00768 
00769   RelatedTextures::const_iterator ti;
00770   ti = _related_textures.find(suffix);
00771   if (ti != _related_textures.end()) {
00772     return (*ti).second;
00773   }
00774   if (cdata->_fullpath.empty()) {
00775     return (Texture*)NULL;
00776   }
00777   Filename main = cdata->_fullpath;
00778   main.set_basename_wo_extension(main.get_basename_wo_extension() +
00779                                  suffix->get_name());
00780   PT(Texture) res;
00781   if (!cdata->_alpha_fullpath.empty()) {
00782     Filename alph = cdata->_alpha_fullpath;
00783     alph.set_basename_wo_extension(alph.get_basename_wo_extension() +
00784                                    suffix->get_name());
00785     VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00786     if (vfs->exists(alph)) {
00787       // The alpha variant of the filename, with the suffix, exists.
00788       // Use it to load the texture.
00789       res = TexturePool::load_texture(main, alph,
00790                                       cdata->_primary_file_num_channels,
00791                                       cdata->_alpha_file_channel, false);
00792     } else {
00793       // If the alpha variant of the filename doesn't exist, just go
00794       // ahead and load the related texture without alpha.
00795       res = TexturePool::load_texture(main);
00796     }
00797 
00798   } else {
00799     // No alpha filename--just load the single file.  It doesn't
00800     // necessarily have the same number of channels as this one.
00801     res = TexturePool::load_texture(main);
00802   }
00803 
00804   // I'm casting away the const-ness of 'this' because this
00805   // field is only a cache.
00806   ((Texture *)this)->_related_textures.insert(RelatedTextures::value_type(suffix, res));
00807   return res;
00808 }
00809 
00810 ////////////////////////////////////////////////////////////////////
00811 //     Function: Texture::get_effective_minfilter
00812 //       Access: Published
00813 //  Description: Returns the filter mode of the texture for
00814 //               minification, with special treatment for FT_default.
00815 //               This will normally not return FT_default, unless
00816 //               there is an error in the config file.
00817 ////////////////////////////////////////////////////////////////////
00818 Texture::FilterType Texture::
00819 get_effective_minfilter() const {
00820   CDReader cdata(_cycler);
00821   if (cdata->_minfilter != FT_default) {
00822     return cdata->_minfilter;
00823   }
00824   if (cdata->_format == Texture::F_depth_stencil ||
00825       cdata->_format == Texture::F_depth_component) {
00826     return FT_nearest;
00827   }
00828   return texture_minfilter;
00829 }
00830 
00831 ////////////////////////////////////////////////////////////////////
00832 //     Function: Texture::get_effective_magfilter
00833 //       Access: Published
00834 //  Description: Returns the filter mode of the texture for
00835 //               magnification, with special treatment for FT_default.
00836 //               This will normally not return FT_default, unless
00837 //               there is an error in the config file.
00838 ////////////////////////////////////////////////////////////////////
00839 Texture::FilterType Texture::
00840 get_effective_magfilter() const {
00841   CDReader cdata(_cycler);
00842   if (cdata->_magfilter != FT_default) {
00843     return cdata->_magfilter;
00844   }
00845   if (cdata->_format == Texture::F_depth_stencil ||
00846       cdata->_format == Texture::F_depth_component) {
00847     return FT_nearest;
00848   }
00849   return texture_magfilter;
00850 }
00851 
00852 ////////////////////////////////////////////////////////////////////
00853 //     Function: Texture::set_ram_image_as
00854 //       Access: Published
00855 //  Description: Replaces the current system-RAM image with the new
00856 //               data, converting it first if necessary from the
00857 //               indicated component-order format.  See
00858 //               get_ram_image_as() for specifications about the
00859 //               format.  This method cannot support compressed image
00860 //               data or sub-pages; use set_ram_image() for that.
00861 ////////////////////////////////////////////////////////////////////
00862 void Texture::
00863 set_ram_image_as(CPTA_uchar image, const string &supplied_format) {
00864   CDWriter cdata(_cycler, true);
00865 
00866   string format = upcase(supplied_format);
00867 
00868   // Make sure we can grab something that's uncompressed.
00869   int imgsize = cdata->_x_size * cdata->_y_size;
00870   nassertv(image.size() == (size_t)(cdata->_component_width * format.size() * imgsize));
00871 
00872   // Check if the format is already what we have internally.
00873   if ((cdata->_num_components == 1 && format.size() == 1) ||
00874       (cdata->_num_components == 2 && format.size() == 2 && format.at(1) == 'A' && format.at(0) != 'A') ||
00875       (cdata->_num_components == 3 && format == "BGR") ||
00876       (cdata->_num_components == 4 && format == "BGRA")) {
00877     // The format string is already our format, so we just need to copy it.
00878     do_set_ram_image(cdata, image);
00879     return;
00880   }
00881 
00882   // Create a new empty array that can hold our image.
00883   PTA_uchar newdata = PTA_uchar::empty_array(imgsize * cdata->_num_components * cdata->_component_width, get_class_type());
00884 
00885   // These ifs are for optimization of commonly used image types.
00886   if (cdata->_component_width == 1) {
00887     if (format == "RGBA" && cdata->_num_components == 4) {
00888       imgsize *= 4;
00889       for (int p = 0; p < imgsize; p += 4) {
00890         newdata[p + 2] = image[p    ];
00891         newdata[p + 1] = image[p + 1];
00892         newdata[p    ] = image[p + 2];
00893         newdata[p + 3] = image[p + 3];
00894       }
00895       do_set_ram_image(cdata, newdata);
00896       return;
00897     }
00898     if (format == "RGB" && cdata->_num_components == 3) {
00899       imgsize *= 3;
00900       for (int p = 0; p < imgsize; p += 3) {
00901         newdata[p + 2] = image[p    ];
00902         newdata[p + 1] = image[p + 1];
00903         newdata[p    ] = image[p + 2];
00904       }
00905       do_set_ram_image(cdata, newdata);
00906       return;
00907     }
00908     if (format == "A" && cdata->_num_components != 3) {
00909       // We can generally rely on alpha to be the last component.
00910       int component = cdata->_num_components - 1;
00911       for (int p = 0; p < imgsize; ++p) {
00912         newdata[component] = image[p];
00913       }
00914       do_set_ram_image(cdata, newdata);
00915       return;
00916     }
00917     for (int p = 0; p < imgsize; ++p) {
00918       for (uchar s = 0; s < format.size(); ++s) {
00919         signed char component = -1;
00920         if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
00921           component = 0;
00922         } else if (format.at(s) == 'G') {
00923           component = 1;
00924         } else if (format.at(s) == 'R') {
00925           component = 2;
00926         } else if (format.at(s) == 'A') {
00927           nassertv(cdata->_num_components != 3);
00928           component = cdata->_num_components - 1;
00929         } else if (format.at(s) == '0') {
00930           // Ignore.
00931         } else if (format.at(s) == '1') {
00932           // Ignore.
00933         } else {
00934           gobj_cat.error() << "Unexpected component character '"
00935             << format.at(s) << "', expected one of RGBA!\n";
00936           return;
00937         }
00938         if (component >= 0) {
00939           newdata[p * cdata->_num_components + component] = image[p * format.size() + s];
00940         }
00941       }
00942     }
00943     do_set_ram_image(cdata, newdata);
00944     return;
00945   }
00946   for (int p = 0; p < imgsize; ++p) {
00947     for (uchar s = 0; s < format.size(); ++s) {
00948       signed char component = -1;
00949       if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
00950         component = 0;
00951       } else if (format.at(s) == 'G') {
00952         component = 1;
00953       } else if (format.at(s) == 'R') {
00954         component = 2;
00955       } else if (format.at(s) == 'A') {
00956         nassertv(cdata->_num_components != 3);
00957         component = cdata->_num_components - 1;
00958       } else if (format.at(s) == '0') {
00959         // Ignore.
00960       } else if (format.at(s) == '1') {
00961         // Ignore.
00962       } else {
00963         gobj_cat.error() << "Unexpected component character '"
00964           << format.at(s) << "', expected one of RGBA!\n";
00965         return;
00966       }
00967       if (component >= 0) {
00968         memcpy((void*)(newdata + (p * cdata->_num_components + component) * cdata->_component_width),
00969                (void*)(image + (p * format.size() + s) * cdata->_component_width),
00970                cdata->_component_width);
00971       }
00972     }
00973   }
00974   do_set_ram_image(cdata, newdata);
00975   return;
00976 }
00977 
00978 ////////////////////////////////////////////////////////////////////
00979 //     Function: Texture::get_keep_ram_image
00980 //       Access: Published, Virtual
00981 //  Description: Returns the flag that indicates whether this Texture
00982 //               is eligible to have its main RAM copy of the texture
00983 //               memory dumped when the texture is prepared for
00984 //               rendering.  See set_keep_ram_image().
00985 ////////////////////////////////////////////////////////////////////
00986 bool Texture::
00987 get_keep_ram_image() const {
00988   CDReader cdata(_cycler);
00989   return cdata->_keep_ram_image;
00990 }
00991 
00992 ////////////////////////////////////////////////////////////////////
00993 //     Function: Texture::is_cacheable
00994 //       Access: Published, Virtual
00995 //  Description: Returns true if there is enough information in this
00996 //               Texture object to write it to the bam cache
00997 //               successfully, false otherwise.  For most textures,
00998 //               this is the same as has_ram_image().
00999 ////////////////////////////////////////////////////////////////////
01000 bool Texture::
01001 is_cacheable() const {
01002   CDReader cdata(_cycler);
01003   return do_has_bam_rawdata(cdata);
01004 }
01005 
01006 ////////////////////////////////////////////////////////////////////
01007 //     Function: Texture::get_num_loadable_ram_mipmap_images
01008 //       Access: Published
01009 //  Description: Returns the number of contiguous mipmap levels that
01010 //               exist in RAM, up until the first gap in the sequence.
01011 //               It is guaranteed that at least mipmap levels [0,
01012 //               get_num_ram_mipmap_images()) exist.
01013 //
01014 //               The number returned will never exceed the number of
01015 //               required mipmap images based on the size of the
01016 //               texture and its filter mode.
01017 //
01018 //               This method is different from
01019 //               get_num_ram_mipmap_images() in that it returns only
01020 //               the number of mipmap levels that can actually be
01021 //               usefully loaded, regardless of the actual number that
01022 //               may be stored.
01023 ////////////////////////////////////////////////////////////////////
01024 int Texture::
01025 get_num_loadable_ram_mipmap_images() const {
01026   CDReader cdata(_cycler);
01027   if (cdata->_ram_images.empty() || cdata->_ram_images[0]._image.empty()) {
01028     // If we don't even have a base image, the answer is none.
01029     return 0;
01030   }
01031   if (!uses_mipmaps()) {
01032     // If we have a base image and don't require mipmapping, the
01033     // answer is 1.
01034     return 1;
01035   }
01036 
01037   // Check that we have enough mipmap levels to meet the size
01038   // requirements.
01039   int size = max(cdata->_x_size, max(cdata->_y_size, cdata->_z_size));
01040   int n = 0;
01041   int x = 1;
01042   while (x < size) {
01043     x = (x << 1);
01044     ++n;
01045     if (n >= (int)cdata->_ram_images.size() || cdata->_ram_images[n]._image.empty()) {
01046       return n;
01047     }
01048   }
01049 
01050   ++n;
01051   return n;
01052 }
01053 
01054 ////////////////////////////////////////////////////////////////////
01055 //     Function: Texture::get_ram_mipmap_image
01056 //       Access: Published
01057 //  Description: Returns the system-RAM image data associated with the
01058 //               nth mipmap level, if present.  Returns NULL if the
01059 //               nth mipmap level is not present.
01060 ////////////////////////////////////////////////////////////////////
01061 CPTA_uchar Texture::
01062 get_ram_mipmap_image(int n) const {
01063   CDReader cdata(_cycler);
01064   if (n < (int)cdata->_ram_images.size() && !cdata->_ram_images[n]._image.empty()) {
01065     return cdata->_ram_images[n]._image;
01066   }
01067   return CPTA_uchar(get_class_type());
01068 }
01069 
01070 ////////////////////////////////////////////////////////////////////
01071 //     Function: Texture::get_ram_mipmap_pointer
01072 //       Access: Published
01073 //  Description: Similiar to get_ram_mipmap_image(), however, in this
01074 //               case the void pointer for the given ram image is
01075 //               returned.  This will be NULL unless it has been
01076 //               explicitly set.
01077 ////////////////////////////////////////////////////////////////////
01078 void *Texture::
01079 get_ram_mipmap_pointer(int n) const {
01080   CDReader cdata(_cycler);
01081   if (n < (int)cdata->_ram_images.size()) {
01082     return cdata->_ram_images[n]._pointer_image;
01083   }
01084   return NULL;
01085 }
01086 
01087 ////////////////////////////////////////////////////////////////////
01088 //     Function: Texture::set_ram_mipmap_pointer
01089 //       Access: Published
01090 //  Description: Sets an explicit void pointer as the texture's mipmap
01091 //               image for the indicated level.  This is a special
01092 //               call to direct a texture to reference some external
01093 //               image location, for instance from a webcam input.
01094 //
01095 //               The texture will henceforth reference this pointer
01096 //               directly, instead of its own internal storage; the
01097 //               user is responsible for ensuring the data at this
01098 //               address remains allocated and valid, and in the
01099 //               correct format, during the lifetime of the texture.
01100 ////////////////////////////////////////////////////////////////////
01101 void Texture::
01102 set_ram_mipmap_pointer(int n, void *image, size_t page_size) {
01103   CDWriter cdata(_cycler, true);
01104   nassertv(cdata->_ram_image_compression != CM_off || do_get_expected_ram_mipmap_image_size(cdata, n));
01105 
01106   while (n >= (int)cdata->_ram_images.size()) {
01107     cdata->_ram_images.push_back(RamImage());
01108   }
01109 
01110   cdata->_ram_images[n]._page_size = page_size;
01111   //_ram_images[n]._image.clear(); wtf is going on?!
01112   cdata->_ram_images[n]._pointer_image = image;
01113   ++(cdata->_image_modified);
01114 }
01115 
01116 ////////////////////////////////////////////////////////////////////
01117 //     Function: Texture::set_ram_mipmap_pointer_from_int
01118 //       Access: Published
01119 //  Description: Accepts a raw pointer cast as an int, which is then
01120 //               passed to set_ram_mipmap_pointer(); see the
01121 //               documentation for that method.
01122 //
01123 //               This variant is particularly useful to set an
01124 //               external pointer from a language like Python, which
01125 //               doesn't support void pointers directly.
01126 ////////////////////////////////////////////////////////////////////
01127 void Texture::
01128 set_ram_mipmap_pointer_from_int(long long pointer, int n, int page_size) {
01129   set_ram_mipmap_pointer(n, (void*)pointer, (size_t)page_size);
01130 }
01131 
01132 ////////////////////////////////////////////////////////////////////
01133 //     Function: Texture::clear_ram_mipmap_image
01134 //       Access: Published
01135 //  Description: Discards the current system-RAM image for the nth
01136 //               mipmap level.
01137 ////////////////////////////////////////////////////////////////////
01138 void Texture::
01139 clear_ram_mipmap_image(int n) {
01140   CDWriter cdata(_cycler, true);
01141   if (n >= (int)cdata->_ram_images.size()) {
01142     return;
01143   }
01144   cdata->_ram_images[n]._page_size = 0;
01145   cdata->_ram_images[n]._image.clear();
01146   cdata->_ram_images[n]._pointer_image = NULL;
01147 }
01148 
01149 ////////////////////////////////////////////////////////////////////
01150 //     Function: Texture::modify_simple_ram_image
01151 //       Access: Published
01152 //  Description: Returns a modifiable pointer to the internal "simple"
01153 //               texture image.  See set_simple_ram_image().
01154 ////////////////////////////////////////////////////////////////////
01155 PTA_uchar Texture::
01156 modify_simple_ram_image() {
01157   CDWriter cdata(_cycler, true);
01158   cdata->_simple_image_date_generated = (PN_int32)time(NULL);
01159   return cdata->_simple_ram_image._image;
01160 }
01161 
01162 ////////////////////////////////////////////////////////////////////
01163 //     Function: Texture::new_simple_ram_image
01164 //       Access: Published
01165 //  Description: Creates an empty array for the simple ram image of
01166 //               the indicated size, and returns a modifiable pointer
01167 //               to the new array.  See set_simple_ram_image().
01168 ////////////////////////////////////////////////////////////////////
01169 PTA_uchar Texture::
01170 new_simple_ram_image(int x_size, int y_size) {
01171   CDWriter cdata(_cycler, true);
01172   nassertr(cdata->_texture_type == TT_2d_texture, PTA_uchar());
01173   size_t expected_page_size = (size_t)(x_size * y_size * 4);
01174 
01175   cdata->_simple_x_size = x_size;
01176   cdata->_simple_y_size = y_size;
01177   cdata->_simple_ram_image._image = PTA_uchar::empty_array(expected_page_size);
01178   cdata->_simple_ram_image._page_size = expected_page_size;
01179   cdata->_simple_image_date_generated = (PN_int32)time(NULL);
01180   ++(cdata->_simple_image_modified);
01181 
01182   return cdata->_simple_ram_image._image;
01183 }
01184 
01185 ////////////////////////////////////////////////////////////////////
01186 //     Function: Texture::generate_simple_ram_image
01187 //       Access: Published
01188 //  Description: Computes the "simple" ram image by loading the main
01189 //               RAM image, if it is not already available, and
01190 //               reducing it to 16x16 or smaller.  This may be an
01191 //               expensive operation.
01192 ////////////////////////////////////////////////////////////////////
01193 void Texture::
01194 generate_simple_ram_image() {
01195   CDWriter cdata(_cycler, true);
01196 
01197   if (cdata->_texture_type != TT_2d_texture ||
01198       cdata->_ram_image_compression != CM_off) {
01199     return;
01200   }
01201 
01202   PNMImage pnmimage;
01203   if (!do_store_one(cdata, pnmimage, 0, 0)) {
01204     return;
01205   }
01206 
01207   // Start at the suggested size from the config file.
01208   int x_size = simple_image_size.get_word(0);
01209   int y_size = simple_image_size.get_word(1);
01210 
01211   // Limit it to no larger than the source image, and also make it a
01212   // power of two.
01213   x_size = down_to_power_2(min(x_size, cdata->_x_size));
01214   y_size = down_to_power_2(min(y_size, cdata->_y_size));
01215 
01216   // Generate a reduced image of that size.
01217   PNMImage scaled(x_size, y_size, pnmimage.get_num_channels());
01218   scaled.quick_filter_from(pnmimage);
01219 
01220   // Make sure the reduced image has 4 components, by convention.
01221   if (!scaled.has_alpha()) {
01222     scaled.add_alpha();
01223     scaled.alpha_fill(1.0);
01224   }
01225   scaled.set_num_channels(4);
01226 
01227   // Now see if we can go even smaller.
01228   bool did_anything;
01229   do {
01230     did_anything = false;
01231 
01232     // Try to reduce X.
01233     if (x_size > 1) {
01234       int new_x_size = (x_size >> 1);
01235       PNMImage smaller(new_x_size, y_size, 4);
01236       smaller.quick_filter_from(scaled);
01237       PNMImage bigger(x_size, y_size, 4);
01238       bigger.quick_filter_from(smaller);
01239 
01240       if (compare_images(scaled, bigger)) {
01241         scaled.take_from(smaller);
01242         x_size = new_x_size;
01243         did_anything = true;
01244       }
01245     }
01246 
01247     // Try to reduce Y.
01248     if (y_size > 1) {
01249       int new_y_size = (y_size >> 1);
01250       PNMImage smaller(x_size, new_y_size, 4);
01251       smaller.quick_filter_from(scaled);
01252       PNMImage bigger(x_size, y_size, 4);
01253       bigger.quick_filter_from(smaller);
01254 
01255       if (compare_images(scaled, bigger)) {
01256         scaled.take_from(smaller);
01257         y_size = new_y_size;
01258         did_anything = true;
01259       }
01260     }
01261   } while (did_anything);
01262 
01263   size_t expected_page_size = (size_t)(x_size * y_size * 4);
01264   PTA_uchar image = PTA_uchar::empty_array(expected_page_size, get_class_type());
01265   convert_from_pnmimage(image, expected_page_size, 0, scaled, 4, 1);
01266 
01267   do_set_simple_ram_image(cdata, image, x_size, y_size);
01268   cdata->_simple_image_date_generated = (PN_int32)time(NULL);
01269 }
01270 
01271 ////////////////////////////////////////////////////////////////////
01272 //     Function: Texture::peek
01273 //       Access: Published
01274 //  Description: Returns a TexturePeeker object that can be used to
01275 //               examine the individual texels stored within this
01276 //               Texture by (u, v) coordinate.
01277 //
01278 //               If the texture has a ram image resident, that image
01279 //               is used.  If it does not have a full ram image but
01280 //               does have a simple_ram_image resident, that image is
01281 //               used instead.  If neither image is resident the full
01282 //               image is reloaded.
01283 //
01284 //               Returns NULL if the texture cannot find an image to
01285 //               load, or the texture format is incompatible.
01286 ////////////////////////////////////////////////////////////////////
01287 PT(TexturePeeker) Texture::
01288 peek() {
01289   CDWriter cdata(_cycler, unlocked_ensure_ram_image(true));
01290 
01291   PT(TexturePeeker) peeker = new TexturePeeker(this, cdata);
01292   if (peeker->is_valid()) {
01293     return peeker;
01294   }
01295 
01296   return NULL;
01297 }
01298 
01299 ////////////////////////////////////////////////////////////////////
01300 //     Function: Texture::prepare
01301 //       Access: Published
01302 //  Description: Indicates that the texture should be enqueued to be
01303 //               prepared in the indicated prepared_objects at the
01304 //               beginning of the next frame.  This will ensure the
01305 //               texture is already loaded into texture memory if it
01306 //               is expected to be rendered soon.
01307 //
01308 //               Use this function instead of prepare_now() to preload
01309 //               textures from a user interface standpoint.
01310 ////////////////////////////////////////////////////////////////////
01311 void Texture::
01312 prepare(PreparedGraphicsObjects *prepared_objects) {
01313   prepared_objects->enqueue_texture(this);
01314 }
01315 
01316 ////////////////////////////////////////////////////////////////////
01317 //     Function: Texture::is_prepared
01318 //       Access: Published
01319 //  Description: Returns true if the texture has already been prepared
01320 //               or enqueued for preparation on the indicated GSG,
01321 //               false otherwise.
01322 ////////////////////////////////////////////////////////////////////
01323 bool Texture::
01324 is_prepared(PreparedGraphicsObjects *prepared_objects) const {
01325   MutexHolder holder(_lock);
01326   PreparedViews::const_iterator pvi;
01327   pvi = _prepared_views.find(prepared_objects);
01328   if (pvi != _prepared_views.end()) {
01329     return true;
01330   }
01331   return prepared_objects->is_texture_queued(this);
01332 }
01333 
01334 ////////////////////////////////////////////////////////////////////
01335 //     Function: Texture::was_image_modified
01336 //       Access: Published
01337 //  Description: Returns true if the texture needs to be re-loaded
01338 //               onto the indicated GSG, either because its image data
01339 //               is out-of-date, or because it's not fully prepared
01340 //               now.
01341 ////////////////////////////////////////////////////////////////////
01342 bool Texture::
01343 was_image_modified(PreparedGraphicsObjects *prepared_objects) const {
01344   MutexHolder holder(_lock);
01345   CDReader cdata(_cycler);
01346 
01347   PreparedViews::const_iterator pvi;
01348   pvi = _prepared_views.find(prepared_objects);
01349   if (pvi != _prepared_views.end()) {
01350     const Contexts &contexts = (*pvi).second;
01351     for (int view = 0; view < cdata->_num_views; ++view) {
01352       Contexts::const_iterator ci;
01353       ci = contexts.find(view);
01354       if (ci == contexts.end()) {
01355         return true;
01356       }
01357       TextureContext *tc = (*ci).second;
01358       if (tc->was_image_modified()) {
01359         return true;
01360       }
01361     }
01362     return false;
01363   }
01364   return true;
01365 }
01366 
01367 ////////////////////////////////////////////////////////////////////
01368 //     Function: Texture::get_data_size_bytes
01369 //       Access: Public
01370 //  Description: Returns the number of bytes which the texture is
01371 //               reported to consume within graphics memory, for the
01372 //               indicated GSG.  This may return a nonzero value even
01373 //               if the texture is not currently resident; you should
01374 //               also check get_resident() if you want to know how
01375 //               much space the texture is actually consuming right
01376 //               now.
01377 ////////////////////////////////////////////////////////////////////
01378 size_t Texture::
01379 get_data_size_bytes(PreparedGraphicsObjects *prepared_objects) const {
01380   MutexHolder holder(_lock);
01381   CDReader cdata(_cycler);
01382 
01383   PreparedViews::const_iterator pvi;
01384   size_t total_size = 0;
01385   pvi = _prepared_views.find(prepared_objects);
01386   if (pvi != _prepared_views.end()) {
01387     const Contexts &contexts = (*pvi).second;
01388     for (int view = 0; view < cdata->_num_views; ++view) {
01389       Contexts::const_iterator ci;
01390       ci = contexts.find(view);
01391       if (ci != contexts.end()) {
01392         TextureContext *tc = (*ci).second;
01393         total_size += tc->get_data_size_bytes();
01394       }
01395     }
01396   }
01397 
01398   return total_size;
01399 }
01400 
01401 ////////////////////////////////////////////////////////////////////
01402 //     Function: Texture::get_active
01403 //       Access: Public
01404 //  Description: Returns true if this Texture was rendered in the most
01405 //               recent frame within the indicated GSG.
01406 ////////////////////////////////////////////////////////////////////
01407 bool Texture::
01408 get_active(PreparedGraphicsObjects *prepared_objects) const {
01409   MutexHolder holder(_lock);
01410   CDReader cdata(_cycler);
01411 
01412   PreparedViews::const_iterator pvi;
01413   pvi = _prepared_views.find(prepared_objects);
01414   if (pvi != _prepared_views.end()) {
01415     const Contexts &contexts = (*pvi).second;
01416     for (int view = 0; view < cdata->_num_views; ++view) {
01417       Contexts::const_iterator ci;
01418       ci = contexts.find(view);
01419       if (ci != contexts.end()) {
01420         TextureContext *tc = (*ci).second;
01421         if (tc->get_active()) {
01422           return true;
01423         }
01424       }
01425     }
01426   }
01427   return false;
01428 }
01429 
01430 ////////////////////////////////////////////////////////////////////
01431 //     Function: Texture::get_resident
01432 //       Access: Public
01433 //  Description: Returns true if this Texture is reported to be
01434 //               resident within graphics memory for the indicated
01435 //               GSG.
01436 ////////////////////////////////////////////////////////////////////
01437 bool Texture::
01438 get_resident(PreparedGraphicsObjects *prepared_objects) const {
01439   MutexHolder holder(_lock);
01440   CDReader cdata(_cycler);
01441 
01442   PreparedViews::const_iterator pvi;
01443   pvi = _prepared_views.find(prepared_objects);
01444   if (pvi != _prepared_views.end()) {
01445     const Contexts &contexts = (*pvi).second;
01446     for (int view = 0; view < cdata->_num_views; ++view) {
01447       Contexts::const_iterator ci;
01448       ci = contexts.find(view);
01449       if (ci != contexts.end()) {
01450         TextureContext *tc = (*ci).second;
01451         if (tc->get_resident()) {
01452           return true;
01453         }
01454       }
01455     }
01456   }
01457   return false;
01458 }
01459 
01460 ////////////////////////////////////////////////////////////////////
01461 //     Function: Texture::release
01462 //       Access: Published
01463 //  Description: Frees the texture context only on the indicated object,
01464 //               if it exists there.  Returns true if it was released,
01465 //               false if it had not been prepared.
01466 ////////////////////////////////////////////////////////////////////
01467 bool Texture::
01468 release(PreparedGraphicsObjects *prepared_objects) {
01469   MutexHolder holder(_lock);
01470   PreparedViews::iterator pvi;
01471   pvi = _prepared_views.find(prepared_objects);
01472   if (pvi != _prepared_views.end()) {
01473     Contexts temp;
01474     temp.swap((*pvi).second);
01475     Contexts::iterator ci;
01476     for (ci = temp.begin(); ci != temp.end(); ++ci) {
01477       TextureContext *tc = (*ci).second;
01478       if (tc != (TextureContext *)NULL) {
01479         prepared_objects->release_texture(tc);
01480       }
01481     }
01482     _prepared_views.erase(pvi);
01483   }
01484 
01485   // Maybe it wasn't prepared yet, but it's about to be.
01486   return prepared_objects->dequeue_texture(this);
01487 }
01488 
01489 ////////////////////////////////////////////////////////////////////
01490 //     Function: Texture::release_all
01491 //       Access: Published
01492 //  Description: Frees the context allocated on all objects for which
01493 //               the texture has been declared.  Returns the number of
01494 //               contexts which have been freed.
01495 ////////////////////////////////////////////////////////////////////
01496 int Texture::
01497 release_all() {
01498   MutexHolder holder(_lock);
01499 
01500   // We have to traverse a copy of the _prepared_views list, because the
01501   // PreparedGraphicsObjects object will call clear_prepared() in response
01502   // to each release_texture(), and we don't want to be modifying the
01503   // _prepared_views list while we're traversing it.
01504   PreparedViews temp;
01505   temp.swap(_prepared_views);
01506   int num_freed = (int)temp.size();
01507 
01508   PreparedViews::iterator pvi;
01509   for (pvi = temp.begin(); pvi != temp.end(); ++pvi) {
01510     PreparedGraphicsObjects *prepared_objects = (*pvi).first;
01511     Contexts temp;
01512     temp.swap((*pvi).second);
01513     Contexts::iterator ci;
01514     for (ci = temp.begin(); ci != temp.end(); ++ci) {
01515       TextureContext *tc = (*ci).second;
01516       if (tc != (TextureContext *)NULL) {
01517         prepared_objects->release_texture(tc);
01518       }
01519     }
01520   }
01521 
01522   return num_freed;
01523 }
01524 
01525 ////////////////////////////////////////////////////////////////////
01526 //     Function: Texture::write
01527 //       Access: Published
01528 //  Description: Not to be confused with write(Filename), this method
01529 //               simply describes the texture properties.
01530 ////////////////////////////////////////////////////////////////////
01531 void Texture::
01532 write(ostream &out, int indent_level) const {
01533   CDReader cdata(_cycler);
01534   indent(out, indent_level)
01535     << cdata->_texture_type << " " << get_name();
01536   if (!cdata->_filename.empty()) {
01537     out << " (from " << cdata->_filename << ")";
01538   }
01539   out << "\n";
01540 
01541   indent(out, indent_level + 2);
01542 
01543   switch (cdata->_texture_type) {
01544   case TT_1d_texture:
01545     out << "1-d, " << cdata->_x_size;
01546     break;
01547 
01548   case TT_2d_texture:
01549     out << "2-d, " << cdata->_x_size << " x " << cdata->_y_size;
01550     break;
01551 
01552   case TT_3d_texture:
01553     out << "3-d, " << cdata->_x_size << " x " << cdata->_y_size << " x " << cdata->_z_size;
01554     break;
01555 
01556   case TT_2d_texture_array:
01557     out << "2-d array, " << cdata->_x_size << " x " << cdata->_y_size << " x " << cdata->_z_size;
01558     break;
01559 
01560   case TT_cube_map:
01561     out << "cube map, " << cdata->_x_size << " x " << cdata->_y_size;
01562     break;
01563   }
01564 
01565   if (cdata->_num_views > 1) {
01566     out << " (x " << cdata->_num_views << " views)";
01567   }
01568 
01569   out << " pixels, each " << cdata->_num_components;
01570 
01571   switch (cdata->_component_type) {
01572   case T_unsigned_byte:
01573     out << " bytes";
01574     break;
01575 
01576   case T_unsigned_short:
01577     out << " shorts";
01578     break;
01579 
01580   case T_float:
01581     out << " floats";
01582     break;
01583 
01584   default:
01585     break;
01586   }
01587 
01588   out << ", ";
01589   switch (cdata->_format) {
01590   case F_color_index:
01591     out << "color_index";
01592     break;
01593   case F_depth_stencil:
01594     out << "depth_stencil";
01595     break;
01596   case F_depth_component:
01597     out << "depth_component";
01598     break;
01599   case F_depth_component16:
01600     out << "depth_component16";
01601     break;
01602   case F_depth_component24:
01603     out << "depth_component24";
01604     break;
01605   case F_depth_component32:
01606     out << "depth_component32";
01607     break;
01608 
01609   case F_rgba:
01610     out << "rgba";
01611     break;
01612   case F_rgbm:
01613     out << "rgbm";
01614     break;
01615   case F_rgba32:
01616     out << "rgba32";
01617     break;
01618   case F_rgba16:
01619     out << "rgba16";
01620     break;
01621   case F_rgba12:
01622     out << "rgba12";
01623     break;
01624   case F_rgba8:
01625     out << "rgba8";
01626     break;
01627   case F_rgba4:
01628     out << "rgba4";
01629     break;
01630 
01631   case F_rgb:
01632     out << "rgb";
01633     break;
01634   case F_rgb12:
01635     out << "rgb12";
01636     break;
01637   case F_rgb8:
01638     out << "rgb8";
01639     break;
01640   case F_rgb5:
01641     out << "rgb5";
01642     break;
01643   case F_rgba5:
01644     out << "rgba5";
01645     break;
01646   case F_rgb332:
01647     out << "rgb332";
01648     break;
01649 
01650   case F_red:
01651     out << "red";
01652     break;
01653   case F_green:
01654     out << "green";
01655     break;
01656   case F_blue:
01657     out << "blue";
01658     break;
01659   case F_alpha:
01660     out << "alpha";
01661     break;
01662   case F_luminance:
01663     out << "luminance";
01664     break;
01665   case F_luminance_alpha:
01666     out << "luminance_alpha";
01667     break;
01668   case F_luminance_alphamask:
01669     out << "luminance_alphamask";
01670     break;
01671   }
01672 
01673   if (cdata->_compression != CM_default) {
01674     out << ", compression " << cdata->_compression;
01675   }
01676   out << "\n";
01677 
01678   indent(out, indent_level + 2);
01679 
01680   switch (cdata->_texture_type) {
01681   case TT_1d_texture:
01682     out << cdata->_wrap_u << ", ";
01683     break;
01684 
01685   case TT_2d_texture:
01686     out << cdata->_wrap_u << " x " << cdata->_wrap_v << ", ";
01687     break;
01688 
01689   case TT_3d_texture:
01690     out << cdata->_wrap_u << " x " << cdata->_wrap_v << " x " << cdata->_wrap_w << ", ";
01691     break;
01692 
01693   case TT_2d_texture_array:
01694     out << cdata->_wrap_u << " x " << cdata->_wrap_v << " x " << cdata->_wrap_w << ", ";
01695     break;
01696 
01697   case TT_cube_map:
01698     break;
01699   }
01700 
01701   out << "min " << cdata->_minfilter
01702       << ", mag " << cdata->_magfilter
01703       << ", aniso " << cdata->_anisotropic_degree
01704       << ", border " << cdata->_border_color
01705       << "\n";
01706 
01707   if (do_has_ram_image(cdata)) {
01708     indent(out, indent_level + 2)
01709       << do_get_ram_image_size(cdata) << " bytes in ram, compression "
01710       << cdata->_ram_image_compression << "\n";
01711 
01712     if (cdata->_ram_images.size() > 1) {
01713       int count = 0;
01714       size_t total_size = 0;
01715       for (size_t n = 1; n < cdata->_ram_images.size(); ++n) {
01716         if (!cdata->_ram_images[n]._image.empty()) {
01717           ++count;
01718           total_size += cdata->_ram_images[n]._image.size();
01719         } else {
01720           // Stop at the first gap.
01721           break;
01722         }
01723       }
01724       indent(out, indent_level + 2)
01725         << count
01726         << " mipmap levels also present in ram (" << total_size
01727         << " bytes).\n";
01728     }
01729 
01730   } else {
01731     indent(out, indent_level + 2)
01732       << "no ram image\n";
01733   }
01734 
01735   if (!cdata->_simple_ram_image._image.empty()) {
01736     indent(out, indent_level + 2)
01737       << "simple image: " << cdata->_simple_x_size << " x "
01738       << cdata->_simple_y_size << ", "
01739       << cdata->_simple_ram_image._image.size() << " bytes\n";
01740   }
01741 }
01742 
01743 
01744 ////////////////////////////////////////////////////////////////////
01745 //     Function: Texture::set_size_padded
01746 //       Access: Published
01747 //  Description: Changes the size of the texture, padding
01748 //               if necessary, and setting the pad region
01749 //               as well.
01750 ////////////////////////////////////////////////////////////////////
01751 void Texture::
01752 set_size_padded(int x, int y, int z) {
01753   CDWriter cdata(_cycler, true);
01754   if (do_get_auto_texture_scale(cdata) != ATS_none) {
01755     do_set_x_size(cdata, up_to_power_2(x));
01756     do_set_y_size(cdata, up_to_power_2(y));
01757     do_set_z_size(cdata, up_to_power_2(z));
01758   } else {
01759     do_set_x_size(cdata, x);
01760     do_set_y_size(cdata, y);
01761     do_set_z_size(cdata, z);
01762   }
01763   do_set_pad_size(cdata, 
01764                   cdata->_x_size - x,
01765                   cdata->_y_size - y,
01766                   cdata->_z_size - z);
01767 }
01768 
01769 ////////////////////////////////////////////////////////////////////
01770 //     Function: Texture::set_orig_file_size
01771 //       Access: Published
01772 //  Description: Specifies the size of the texture as it exists in its
01773 //               original disk file, before any Panda scaling.
01774 ////////////////////////////////////////////////////////////////////
01775 void Texture::
01776 set_orig_file_size(int x, int y, int z) {
01777   CDWriter cdata(_cycler, true);
01778   cdata->_orig_file_x_size = x;
01779   cdata->_orig_file_y_size = y;
01780 
01781   nassertv(z == cdata->_z_size);
01782 }
01783 
01784 ////////////////////////////////////////////////////////////////////
01785 //     Function: Texture::is_mipmap
01786 //       Access: Published, Static
01787 //  Description: Returns true if the indicated filter type requires
01788 //               the use of mipmaps, or false if it does not.
01789 ////////////////////////////////////////////////////////////////////
01790 bool Texture::
01791 is_mipmap(FilterType filter_type) {
01792   switch (filter_type) {
01793   case FT_nearest_mipmap_nearest:
01794   case FT_linear_mipmap_nearest:
01795   case FT_nearest_mipmap_linear:
01796   case FT_linear_mipmap_linear:
01797     return true;
01798 
01799   default:
01800     return false;
01801   }
01802 }
01803 
01804 ////////////////////////////////////////////////////////////////////
01805 //     Function: Texture::prepare_now
01806 //       Access: Published
01807 //  Description: Creates a context for the texture on the particular
01808 //               GSG, if it does not already exist.  Returns the new
01809 //               (or old) TextureContext.  This assumes that the
01810 //               GraphicsStateGuardian is the currently active
01811 //               rendering context and that it is ready to accept new
01812 //               textures.  If this is not necessarily the case, you
01813 //               should use prepare() instead.
01814 //
01815 //               Normally, this is not called directly except by the
01816 //               GraphicsStateGuardian; a texture does not need to be
01817 //               explicitly prepared by the user before it may be
01818 //               rendered.
01819 ////////////////////////////////////////////////////////////////////
01820 TextureContext *Texture::
01821 prepare_now(int view,
01822             PreparedGraphicsObjects *prepared_objects,
01823             GraphicsStateGuardianBase *gsg) {
01824   MutexHolder holder(_lock);
01825   CDReader cdata(_cycler);
01826 
01827   // Don't exceed the actual number of views.
01828   view = max(min(view, cdata->_num_views - 1), 0);
01829 
01830   // Get the list of PreparedGraphicsObjects for this view.
01831   Contexts &contexts = _prepared_views[prepared_objects];
01832   Contexts::const_iterator pvi;
01833   pvi = contexts.find(view);
01834   if (pvi != contexts.end()) {
01835     return (*pvi).second;
01836   }
01837 
01838   TextureContext *tc = prepared_objects->prepare_texture_now(this, view, gsg);
01839   contexts[view] = tc;
01840 
01841   return tc;
01842 }
01843 
01844 ////////////////////////////////////////////////////////////////////
01845 //     Function: Texture::up_to_power_2
01846 //       Access: Published, Static
01847 //  Description: Returns the smallest power of 2 greater than or equal
01848 //               to value.
01849 ////////////////////////////////////////////////////////////////////
01850 int Texture::
01851 up_to_power_2(int value) {
01852   if (value <= 1) {
01853     return 1;
01854   }
01855   int bit = get_next_higher_bit(((unsigned int)value) - 1);
01856   return (1 << bit);
01857 }
01858 
01859 ////////////////////////////////////////////////////////////////////
01860 //     Function: Texture::down_to_power_2
01861 //       Access: Published, Static
01862 //  Description: Returns the largest power of 2 less than or equal
01863 //               to value.
01864 ////////////////////////////////////////////////////////////////////
01865 int Texture::
01866 down_to_power_2(int value) {
01867   if (value <= 1) {
01868     return 1;
01869   }
01870   int bit = get_next_higher_bit(((unsigned int)value) >> 1);
01871   return (1 << bit);
01872 }
01873 
01874 ////////////////////////////////////////////////////////////////////
01875 //     Function: Texture::consider_rescale
01876 //       Access: Published
01877 //  Description: Asks the PNMImage to change its scale when it reads
01878 //               the image, according to the whims of the Config.prc
01879 //               file.
01880 //
01881 //               This method should be called after
01882 //               pnmimage.read_header() has been called, but before
01883 //               pnmimage.read().  Also see rescale_texture().
01884 ////////////////////////////////////////////////////////////////////
01885 void Texture::
01886 consider_rescale(PNMImage &pnmimage) {
01887   consider_rescale(pnmimage, get_name(), get_auto_texture_scale());
01888 }
01889 
01890 ////////////////////////////////////////////////////////////////////
01891 //     Function: Texture::consider_rescale
01892 //       Access: Published, Static
01893 //  Description: Asks the PNMImage to change its scale when it reads
01894 //               the image, according to the whims of the Config.prc
01895 //               file.
01896 //
01897 //               This method should be called after
01898 //               pnmimage.read_header() has been called, but before
01899 //               pnmimage.read().  Also see rescale_texture().
01900 ////////////////////////////////////////////////////////////////////
01901 void Texture::
01902 consider_rescale(PNMImage &pnmimage, const string &name, AutoTextureScale auto_texture_scale) {
01903   int new_x_size = pnmimage.get_x_size();
01904   int new_y_size = pnmimage.get_y_size();
01905   if (adjust_size(new_x_size, new_y_size, name, false, auto_texture_scale)) {
01906     pnmimage.set_read_size(new_x_size, new_y_size);
01907   }
01908 }
01909 
01910 ////////////////////////////////////////////////////////////////////
01911 //     Function: Texture::format_texture_type
01912 //       Access: Published, Static
01913 //  Description: Returns the indicated TextureType converted to a
01914 //               string word.
01915 ////////////////////////////////////////////////////////////////////
01916 string Texture::
01917 format_texture_type(TextureType tt) {
01918   switch (tt) {
01919   case TT_1d_texture:
01920     return "1d_texture";
01921   case TT_2d_texture:
01922     return "2d_texture";
01923   case TT_3d_texture:
01924     return "3d_texture";
01925   case TT_2d_texture_array:
01926     return "2d_texture_array";
01927   case TT_cube_map:
01928     return "cube_map";
01929   }
01930   return "**invalid**";
01931 }
01932 
01933 ////////////////////////////////////////////////////////////////////
01934 //     Function: Texture::string_texture_type
01935 //       Access: Published, Static
01936 //  Description: Returns the TextureType corresponding to the
01937 //               indicated string word.
01938 ////////////////////////////////////////////////////////////////////
01939 Texture::TextureType Texture::
01940 string_texture_type(const string &str) {
01941   if (cmp_nocase(str, "1d_texture") == 0) {
01942     return TT_1d_texture;
01943   } else if (cmp_nocase(str, "2d_texture") == 0) {
01944     return TT_2d_texture;
01945   } else if (cmp_nocase(str, "3d_texture") == 0) {
01946     return TT_3d_texture;
01947   } else if (cmp_nocase(str, "2d_texture_array") == 0) {
01948     return TT_2d_texture_array;
01949   } else if (cmp_nocase(str, "cube_map") == 0) {
01950     return TT_cube_map;
01951   }
01952 
01953   gobj_cat->error()
01954     << "Invalid Texture::TextureType value: " << str << "\n";
01955   return TT_2d_texture;
01956 }
01957 
01958 ////////////////////////////////////////////////////////////////////
01959 //     Function: Texture::format_component_type
01960 //       Access: Published, Static
01961 //  Description: Returns the indicated ComponentType converted to a
01962 //               string word.
01963 ////////////////////////////////////////////////////////////////////
01964 string Texture::
01965 format_component_type(ComponentType ct) {
01966   switch (ct) {
01967   case T_unsigned_byte:
01968     return "unsigned_byte";
01969   case T_unsigned_short:
01970     return "unsigned_short";
01971   case T_float:
01972     return "float";
01973   case T_unsigned_int_24_8:
01974     return "unsigned_int_24_8";
01975   }
01976 
01977   return "**invalid**";
01978 }
01979 
01980 ////////////////////////////////////////////////////////////////////
01981 //     Function: Texture::string_component_type
01982 //       Access: Published, Static
01983 //  Description: Returns the ComponentType corresponding to the
01984 //               indicated string word.
01985 ////////////////////////////////////////////////////////////////////
01986 Texture::ComponentType Texture::
01987 string_component_type(const string &str) {
01988   if (cmp_nocase(str, "unsigned_byte") == 0) {
01989     return T_unsigned_byte;
01990   } else if (cmp_nocase(str, "unsigned_short") == 0) {
01991     return T_unsigned_short;
01992   } else if (cmp_nocase(str, "float") == 0) {
01993     return T_float;
01994   } else if (cmp_nocase(str, "unsigned_int_24_8") == 0) {
01995     return T_unsigned_int_24_8;
01996   }
01997 
01998   gobj_cat->error()
01999     << "Invalid Texture::ComponentType value: " << str << "\n";
02000   return T_unsigned_byte;
02001 }
02002 
02003 ////////////////////////////////////////////////////////////////////
02004 //     Function: Texture::format_format
02005 //       Access: Published, Static
02006 //  Description: Returns the indicated Format converted to a
02007 //               string word.
02008 ////////////////////////////////////////////////////////////////////
02009 string Texture::
02010 format_format(Format format) {
02011   switch (format) {
02012   case F_depth_stencil:
02013     return "depth_stencil";
02014   case F_depth_component:
02015     return "depth_component";
02016   case F_depth_component16:
02017     return "depth_component16";
02018   case F_depth_component24:
02019     return "depth_component24";
02020   case F_depth_component32:
02021     return "depth_component32";
02022   case F_color_index:
02023     return "color_index";
02024   case F_red:
02025     return "red";
02026   case F_green:
02027     return "green";
02028   case F_blue:
02029     return "blue";
02030   case F_alpha:
02031     return "alpha";
02032   case F_rgb:
02033     return "rgb";
02034   case F_rgb5:
02035     return "rgb5";
02036   case F_rgb8:
02037     return "rgb8";
02038   case F_rgb12:
02039     return "rgb12";
02040   case F_rgb332:
02041     return "rgb332";
02042   case F_rgba:
02043     return "rgba";
02044   case F_rgbm:
02045     return "rgbm";
02046   case F_rgba4:
02047     return "rgba4";
02048   case F_rgba5:
02049     return "rgba5";
02050   case F_rgba8:
02051     return "rgba8";
02052   case F_rgba12:
02053     return "rgba12";
02054   case F_luminance:
02055     return "luminance";
02056   case F_luminance_alpha:
02057     return "luminance_alpha";
02058   case F_luminance_alphamask:
02059     return "luminance_alphamask";
02060   case F_rgba16:
02061     return "rgba16";
02062   case F_rgba32:
02063     return "rgba32";
02064   }
02065   return "**invalid**";
02066 }
02067 
02068 ////////////////////////////////////////////////////////////////////
02069 //     Function: Texture::string_format
02070 //       Access: Published, Static
02071 //  Description: Returns the Format corresponding to the
02072 //               indicated string word.
02073 ////////////////////////////////////////////////////////////////////
02074 Texture::Format Texture::
02075 string_format(const string &str) {
02076   if (cmp_nocase(str, "depth_stencil") == 0) {
02077     return F_depth_stencil;
02078   } else if (cmp_nocase(str, "depth_component") == 0) {
02079     return F_depth_component;
02080   } else if (cmp_nocase(str, "depth_component16") == 0 || cmp_nocase(str, "d16") == 0) {
02081     return F_depth_component16;
02082   } else if (cmp_nocase(str, "depth_component24") == 0 || cmp_nocase(str, "d24") == 0) {
02083     return F_depth_component24;
02084   } else if (cmp_nocase(str, "depth_component32") == 0 || cmp_nocase(str, "d32") == 0) {
02085     return F_depth_component32;
02086   } else if (cmp_nocase(str, "color_index") == 0) {
02087     return F_color_index;
02088   } else if (cmp_nocase(str, "red") == 0) {
02089     return F_red;
02090   } else if (cmp_nocase(str, "green") == 0) {
02091     return F_green;
02092   } else if (cmp_nocase(str, "blue") == 0) {
02093     return F_blue;
02094   } else if (cmp_nocase(str, "alpha") == 0) {
02095     return F_alpha;
02096   } else if (cmp_nocase(str, "rgb") == 0) {
02097     return F_rgb;
02098   } else if (cmp_nocase(str, "rgb5") == 0) {
02099     return F_rgb5;
02100   } else if (cmp_nocase(str, "rgb8") == 0 || cmp_nocase(str, "r8g8b8") == 0) {
02101     return F_rgb8;
02102   } else if (cmp_nocase(str, "rgb12") == 0) {
02103     return F_rgb12;
02104   } else if (cmp_nocase(str, "rgb332") == 0 || cmp_nocase(str, "r3g3b2") == 0) {
02105     return F_rgb332;
02106   } else if (cmp_nocase(str, "rgba") == 0) {
02107     return F_rgba;
02108   } else if (cmp_nocase(str, "rgbm") == 0) {
02109     return F_rgbm;
02110   } else if (cmp_nocase(str, "rgba4") == 0) {
02111     return F_rgba4;
02112   } else if (cmp_nocase(str, "rgba5") == 0) {
02113     return F_rgba5;
02114   } else if (cmp_nocase(str, "rgba8") == 0 || cmp_nocase(str, "r8g8b8a8") == 0) {
02115     return F_rgba8;
02116   } else if (cmp_nocase(str, "rgba12") == 0) {
02117     return F_rgba12;
02118   } else if (cmp_nocase(str, "luminance") == 0) {
02119     return F_luminance;
02120   } else if (cmp_nocase(str, "luminance_alpha") == 0) {
02121     return F_luminance_alpha;
02122   } else if (cmp_nocase(str, "luminance_alphamask") == 0) {
02123     return F_luminance_alphamask;
02124   } else if (cmp_nocase(str, "rgba16") == 0 || cmp_nocase(str, "r16g16b16a16") == 0) {
02125     return F_rgba16;
02126   } else if (cmp_nocase(str, "rgba32") == 0 || cmp_nocase(str, "r32g32b32a32") == 0) {
02127     return F_rgba32;
02128   }
02129 
02130   gobj_cat->error()
02131     << "Invalid Texture::Format value: " << str << "\n";
02132   return F_rgba;
02133 }
02134 
02135 ////////////////////////////////////////////////////////////////////
02136 //     Function: Texture::format_filter_type
02137 //       Access: Published, Static
02138 //  Description: Returns the indicated FilterType converted to a
02139 //               string word.
02140 ////////////////////////////////////////////////////////////////////
02141 string Texture::
02142 format_filter_type(FilterType ft) {
02143   switch (ft) {
02144   case FT_nearest:
02145     return "nearest";
02146   case FT_linear:
02147     return "linear";
02148 
02149   case FT_nearest_mipmap_nearest:
02150     return "nearest_mipmap_nearest";
02151   case FT_linear_mipmap_nearest:
02152     return "linear_mipmap_nearest";
02153   case FT_nearest_mipmap_linear:
02154     return "nearest_mipmap_linear";
02155   case FT_linear_mipmap_linear:
02156     return "linear_mipmap_linear";
02157 
02158   case FT_shadow:
02159     return "shadow";
02160 
02161   case FT_default:
02162     return "default";
02163 
02164   case FT_invalid:
02165     return "invalid";
02166   }
02167   return "**invalid**";
02168 }
02169 
02170 ////////////////////////////////////////////////////////////////////
02171 //     Function: Texture::string_filter_type
02172 //       Access: Public
02173 //  Description: Returns the FilterType value associated with the given
02174 //               string representation, or FT_invalid if the string
02175 //               does not match any known FilterType value.
02176 ////////////////////////////////////////////////////////////////////
02177 Texture::FilterType Texture::
02178 string_filter_type(const string &string) {
02179   if (cmp_nocase_uh(string, "nearest") == 0) {
02180     return FT_nearest;
02181   } else if (cmp_nocase_uh(string, "linear") == 0) {
02182     return FT_linear;
02183   } else if (cmp_nocase_uh(string, "nearest_mipmap_nearest") == 0) {
02184     return FT_nearest_mipmap_nearest;
02185   } else if (cmp_nocase_uh(string, "linear_mipmap_nearest") == 0) {
02186     return FT_linear_mipmap_nearest;
02187   } else if (cmp_nocase_uh(string, "nearest_mipmap_linear") == 0) {
02188     return FT_nearest_mipmap_linear;
02189   } else if (cmp_nocase_uh(string, "linear_mipmap_linear") == 0) {
02190     return FT_linear_mipmap_linear;
02191   } else if (cmp_nocase_uh(string, "mipmap") == 0) {
02192     return FT_linear_mipmap_linear;
02193   } else if (cmp_nocase_uh(string, "shadow") == 0) {
02194     return FT_shadow;
02195   } else if (cmp_nocase_uh(string, "default") == 0) {
02196     return FT_default;
02197   } else {
02198     return FT_invalid;
02199   }
02200 }
02201 
02202 ////////////////////////////////////////////////////////////////////
02203 //     Function: Texture::format_wrap_mode
02204 //       Access: Published, Static
02205 //  Description: Returns the indicated WrapMode converted to a
02206 //               string word.
02207 ////////////////////////////////////////////////////////////////////
02208 string Texture::
02209 format_wrap_mode(WrapMode wm) {
02210   switch (wm) {
02211   case WM_clamp:
02212     return "clamp";
02213   case WM_repeat:
02214     return "repeat";
02215   case WM_mirror:
02216     return "mirror";
02217   case WM_mirror_once:
02218     return "mirror_once";
02219   case WM_border_color:
02220     return "border_color";
02221 
02222   case WM_invalid:
02223     return "invalid";
02224   }
02225 
02226   return "**invalid**";
02227 }
02228 
02229 ////////////////////////////////////////////////////////////////////
02230 //     Function: Texture::string_wrap_mode
02231 //       Access: Public
02232 //  Description: Returns the WrapMode value associated with the given
02233 //               string representation, or WM_invalid if the string
02234 //               does not match any known WrapMode value.
02235 ////////////////////////////////////////////////////////////////////
02236 Texture::WrapMode Texture::
02237 string_wrap_mode(const string &string) {
02238   if (cmp_nocase_uh(string, "repeat") == 0 ||
02239       cmp_nocase_uh(string, "wrap") == 0) {
02240     return WM_repeat;
02241   } else if (cmp_nocase_uh(string, "clamp") == 0) {
02242     return WM_clamp;
02243   } else if (cmp_nocase_uh(string, "mirror") == 0 ||
02244              cmp_nocase_uh(string, "mirrored_repeat") == 0) {
02245     return WM_mirror;
02246   } else if (cmp_nocase_uh(string, "mirror_once") == 0) {
02247     return WM_mirror_once;
02248   } else if (cmp_nocase_uh(string, "border_color") == 0 ||
02249              cmp_nocase_uh(string, "border") == 0) {
02250     return WM_border_color;
02251   } else {
02252     return WM_invalid;
02253   }
02254 }
02255 
02256 ////////////////////////////////////////////////////////////////////
02257 //     Function: Texture::format_compression_mode
02258 //       Access: Published, Static
02259 //  Description: Returns the indicated CompressionMode converted to a
02260 //               string word.
02261 ////////////////////////////////////////////////////////////////////
02262 string Texture::
02263 format_compression_mode(CompressionMode cm) {
02264   switch (cm) {
02265   case CM_default:
02266     return "default";
02267   case CM_off:
02268     return "off";
02269   case CM_on:
02270     return "on";
02271   case CM_fxt1:
02272     return "fxt1";
02273   case CM_dxt1:
02274     return "dxt1";
02275   case CM_dxt2:
02276     return "dxt2";
02277   case CM_dxt3:
02278     return "dxt3";
02279   case CM_dxt4:
02280     return "dxt4";
02281   case CM_dxt5:
02282     return "dxt5";
02283   case CM_pvr1_2bpp:
02284     return "pvr1_2bpp";
02285   case CM_pvr1_4bpp:
02286     return "pvr1_4bpp";
02287   }
02288 
02289   return "**invalid**";
02290 }
02291 
02292 ////////////////////////////////////////////////////////////////////
02293 //     Function: Texture::string_compression_mode
02294 //       Access: Public
02295 //  Description: Returns the CompressionMode value associated with the
02296 //               given string representation.
02297 ////////////////////////////////////////////////////////////////////
02298 Texture::CompressionMode Texture::
02299 string_compression_mode(const string &str) {
02300   if (cmp_nocase_uh(str, "default") == 0) {
02301     return CM_default;
02302   } else if (cmp_nocase_uh(str, "off") == 0) {
02303     return CM_off;
02304   } else if (cmp_nocase_uh(str, "on") == 0) {
02305     return CM_on;
02306   } else if (cmp_nocase_uh(str, "fxt1") == 0) {
02307     return CM_fxt1;
02308   } else if (cmp_nocase_uh(str, "dxt1") == 0) {
02309     return CM_dxt1;
02310   } else if (cmp_nocase_uh(str, "dxt2") == 0) {
02311     return CM_dxt2;
02312   } else if (cmp_nocase_uh(str, "dxt3") == 0) {
02313     return CM_dxt3;
02314   } else if (cmp_nocase_uh(str, "dxt4") == 0) {
02315     return CM_dxt4;
02316   } else if (cmp_nocase_uh(str, "dxt5") == 0) {
02317     return CM_dxt5;
02318   } else if (cmp_nocase_uh(str, "pvr1_2bpp") == 0) {
02319     return CM_pvr1_2bpp;
02320   } else if (cmp_nocase_uh(str, "pvr1_4bpp") == 0) {
02321     return CM_pvr1_4bpp;
02322   }
02323 
02324   gobj_cat->error()
02325     << "Invalid Texture::CompressionMode value: " << str << "\n";
02326   return CM_default;
02327 }
02328 
02329 
02330 ////////////////////////////////////////////////////////////////////
02331 //     Function: Texture::format_quality_level
02332 //       Access: Published, Static
02333 //  Description: Returns the indicated QualityLevel converted to a
02334 //               string word.
02335 ////////////////////////////////////////////////////////////////////
02336 string Texture::
02337 format_quality_level(QualityLevel ql) {
02338   switch (ql) {
02339   case QL_default:
02340     return "default";
02341   case QL_fastest:
02342     return "fastest";
02343   case QL_normal:
02344     return "normal";
02345   case QL_best:
02346     return "best";
02347   }
02348 
02349   return "**invalid**";
02350 }
02351 
02352 ////////////////////////////////////////////////////////////////////
02353 //     Function: Texture::string_quality_level
02354 //       Access: Public
02355 //  Description: Returns the QualityLevel value associated with the
02356 //               given string representation.
02357 ////////////////////////////////////////////////////////////////////
02358 Texture::QualityLevel Texture::
02359 string_quality_level(const string &str) {
02360   if (cmp_nocase(str, "default") == 0) {
02361     return QL_default;
02362   } else if (cmp_nocase(str, "fastest") == 0) {
02363     return QL_fastest;
02364   } else if (cmp_nocase(str, "normal") == 0) {
02365     return QL_normal;
02366   } else if (cmp_nocase(str, "best") == 0) {
02367     return QL_best;
02368   }
02369 
02370   gobj_cat->error()
02371     << "Invalid Texture::QualityLevel value: " << str << "\n";
02372   return QL_default;
02373 }
02374 
02375 ////////////////////////////////////////////////////////////////////
02376 //     Function: Texture::texture_uploaded
02377 //       Access: Public
02378 //  Description: This method is called by the GraphicsEngine at the
02379 //               beginning of the frame *after* a texture has been
02380 //               successfully uploaded to graphics memory.  It is
02381 //               intended as a callback so the texture can release its
02382 //               RAM image, if _keep_ram_image is false.
02383 //
02384 //               This is called indirectly when the GSG calls
02385 //               GraphicsEngine::texture_uploaded().
02386 ////////////////////////////////////////////////////////////////////
02387 void Texture::
02388 texture_uploaded() {
02389   CDLockedReader cdata(_cycler);
02390 
02391   if (!keep_texture_ram && !cdata->_keep_ram_image) {
02392     // Once we have prepared the texture, we can generally safely
02393     // remove the pixels from main RAM.  The GSG is now responsible
02394     // for remembering what it looks like.
02395 
02396     CDWriter cdataw(_cycler, cdata, false);
02397     if (gobj_cat.is_debug()) {
02398       gobj_cat.debug()
02399         << "Dumping RAM for texture " << get_name() << "\n";
02400     }
02401     do_clear_ram_image(cdataw);
02402   }
02403 }
02404 
02405 ////////////////////////////////////////////////////////////////////
02406 //     Function: Texture::has_cull_callback
02407 //       Access: Public, Virtual
02408 //  Description: Should be overridden by derived classes to return
02409 //               true if cull_callback() has been defined.  Otherwise,
02410 //               returns false to indicate cull_callback() does not
02411 //               need to be called for this node during the cull
02412 //               traversal.
02413 ////////////////////////////////////////////////////////////////////
02414 bool Texture::
02415 has_cull_callback() const {
02416   return false;
02417 }
02418 
02419 ////////////////////////////////////////////////////////////////////
02420 //     Function: Texture::cull_callback
02421 //       Access: Public, Virtual
02422 //  Description: If has_cull_callback() returns true, this function
02423 //               will be called during the cull traversal to perform
02424 //               any additional operations that should be performed at
02425 //               cull time.
02426 //
02427 //               This is called each time the Texture is discovered
02428 //               applied to a Geom in the traversal.  It should return
02429 //               true if the Geom is visible, false if it should be
02430 //               omitted.
02431 ////////////////////////////////////////////////////////////////////
02432 bool Texture::
02433 cull_callback(CullTraverser *, const CullTraverserData &) const {
02434   return true;
02435 }
02436 
02437 ////////////////////////////////////////////////////////////////////
02438 //     Function: Texture::make_texture
02439 //       Access: Public, Static
02440 //  Description: A factory function to make a new Texture, used to
02441 //               pass to the TexturePool.
02442 ////////////////////////////////////////////////////////////////////
02443 PT(Texture) Texture::
02444 make_texture() {
02445   return new Texture;
02446 }
02447 
02448 ////////////////////////////////////////////////////////////////////
02449 //     Function: Texture::is_specific
02450 //       Access: Public, Static
02451 //  Description: Returns true if the indicated compression mode is one
02452 //               of the specific compression types, false otherwise.
02453 ////////////////////////////////////////////////////////////////////
02454 bool Texture::
02455 is_specific(Texture::CompressionMode compression) {
02456   switch (compression) {
02457   case CM_default:
02458   case CM_off:
02459   case CM_on:
02460     return false;
02461 
02462   default:
02463     return true;
02464   }
02465 }
02466 
02467 ////////////////////////////////////////////////////////////////////
02468 //     Function: Texture::has_alpha
02469 //       Access: Public, Static
02470 //  Description: Returns true if the indicated format includes alpha,
02471 //               false otherwise.
02472 ////////////////////////////////////////////////////////////////////
02473 bool Texture::
02474 has_alpha(Format format) {
02475   switch (format) {
02476   case F_alpha:
02477   case F_rgba:
02478   case F_rgbm:
02479   case F_rgba4:
02480   case F_rgba5:
02481   case F_rgba8:
02482   case F_rgba12:
02483   case F_rgba16:
02484   case F_rgba32:
02485   case F_luminance_alpha:
02486   case F_luminance_alphamask:
02487     return true;
02488 
02489   default:
02490     return false;
02491   }
02492 }
02493 
02494 ////////////////////////////////////////////////////////////////////
02495 //     Function: Texture::has_binary_alpha
02496 //       Access: Public, Static
02497 //  Description: Returns true if the indicated format includes a
02498 //               binary alpha only, false otherwise.
02499 ////////////////////////////////////////////////////////////////////
02500 bool Texture::
02501 has_binary_alpha(Format format) {
02502   switch (format) {
02503   case F_rgbm:
02504     return true;
02505 
02506   default:
02507     return false;
02508   }
02509 }
02510 
02511 ////////////////////////////////////////////////////////////////////
02512 //     Function: Texture::adjust_size
02513 //       Access: Public, Static
02514 //  Description: Computes the proper size of the texture, based on the
02515 //               original size, the filename, and the resizing whims
02516 //               of the config file.
02517 //
02518 //               x_size and y_size should be loaded with the texture
02519 //               image's original size on disk.  On return, they will
02520 //               be loaded with the texture's in-memory target size.
02521 //               The return value is true if the size has been
02522 //               adjusted, or false if it is the same.
02523 ////////////////////////////////////////////////////////////////////
02524 bool Texture::
02525 adjust_size(int &x_size, int &y_size, const string &name,
02526             bool for_padding, AutoTextureScale auto_texture_scale) {
02527   bool exclude = false;
02528   int num_excludes = exclude_texture_scale.get_num_unique_values();
02529   for (int i = 0; i < num_excludes && !exclude; ++i) {
02530     GlobPattern pat(exclude_texture_scale.get_unique_value(i));
02531     if (pat.matches(name)) {
02532       exclude = true;
02533     }
02534   }
02535 
02536   int new_x_size = x_size;
02537   int new_y_size = y_size;
02538 
02539   if (!exclude) {
02540     new_x_size = (int)cfloor(new_x_size * texture_scale + 0.5);
02541     new_y_size = (int)cfloor(new_y_size * texture_scale + 0.5);
02542 
02543     // Don't auto-scale below 4 in either dimension.  This causes
02544     // problems for DirectX and texture compression.
02545     new_x_size = min(max(new_x_size, (int)texture_scale_limit), x_size);
02546     new_y_size = min(max(new_y_size, (int)texture_scale_limit), y_size);
02547   }
02548 
02549   AutoTextureScale ats = auto_texture_scale;
02550   if (ats == ATS_unspecified) {
02551     ats = get_textures_power_2();
02552   }
02553   if (!for_padding && ats == ATS_pad) {
02554     // If we're not calculating the padding size--that is, we're
02555     // calculating the initial scaling size instead--then ignore
02556     // ATS_pad, and treat it the same as ATS_none.
02557     ats = ATS_none;
02558   }
02559 
02560   switch (ats) {
02561   case ATS_down:
02562     new_x_size = down_to_power_2(new_x_size);
02563     new_y_size = down_to_power_2(new_y_size);
02564     break;
02565 
02566   case ATS_up:
02567   case ATS_pad:
02568     new_x_size = up_to_power_2(new_x_size);
02569     new_y_size = up_to_power_2(new_y_size);
02570     break;
02571 
02572   case ATS_none:
02573   case ATS_unspecified:
02574     break;
02575   }
02576 
02577   ats = textures_square.get_value();
02578   if (!for_padding && ats == ATS_pad) {
02579     ats = ATS_none;
02580   }
02581   switch (ats) {
02582   case ATS_down:
02583     new_x_size = new_y_size = min(new_x_size, new_y_size);
02584     break;
02585 
02586   case ATS_up:
02587   case ATS_pad:
02588     new_x_size = new_y_size = max(new_x_size, new_y_size);
02589     break;
02590 
02591   case ATS_none:
02592   case ATS_unspecified:
02593     break;
02594   }
02595 
02596   if (!exclude) {
02597     int max_dimension = max_texture_dimension;
02598 
02599     if (max_dimension < 0) {
02600       GraphicsStateGuardianBase *gsg = GraphicsStateGuardianBase::get_default_gsg();
02601       if (gsg != (GraphicsStateGuardianBase *)NULL) {
02602         max_dimension = gsg->get_max_texture_dimension();
02603       }
02604     }
02605 
02606     if (max_dimension > 0) {
02607       new_x_size = min(new_x_size, (int)max_dimension);
02608       new_y_size = min(new_y_size, (int)max_dimension);
02609     }
02610   }
02611 
02612   if (x_size != new_x_size || y_size != new_y_size) {
02613     x_size = new_x_size;
02614     y_size = new_y_size;
02615     return true;
02616   }
02617 
02618   return false;
02619 }
02620 
02621 ////////////////////////////////////////////////////////////////////
02622 //     Function: Texture::reconsider_dirty
02623 //       Access: Protected, Virtual
02624 //  Description: Called by TextureContext to give the Texture a chance
02625 //               to mark itself dirty before rendering, if necessary.
02626 ////////////////////////////////////////////////////////////////////
02627 void Texture::
02628 reconsider_dirty() {
02629 }
02630 
02631 ////////////////////////////////////////////////////////////////////
02632 //     Function: Texture::do_adjust_this_size
02633 //       Access: Protected, Virtual
02634 //  Description: Works like adjust_size, but also considers the
02635 //               texture class.  Movie textures, for instance, always
02636 //               pad outwards, regardless of textures-power-2.
02637 ////////////////////////////////////////////////////////////////////
02638 bool Texture::
02639 do_adjust_this_size(const CData *cdata, int &x_size, int &y_size, const string &name,
02640                     bool for_padding) const {
02641   return adjust_size(x_size, y_size, name, for_padding, cdata->_auto_texture_scale);
02642 }
02643 
02644 ////////////////////////////////////////////////////////////////////
02645 //     Function: Texture::do_read
02646 //       Access: Protected, Virtual
02647 //  Description: The internal implementation of the various read()
02648 //               methods.
02649 ////////////////////////////////////////////////////////////////////
02650 bool Texture::
02651 do_read(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpath,
02652         int primary_file_num_channels, int alpha_file_channel,
02653         int z, int n, bool read_pages, bool read_mipmaps,
02654         const LoaderOptions &options, BamCacheRecord *record) {
02655   PStatTimer timer(_texture_read_pcollector);
02656 
02657   if (options.get_auto_texture_scale() != ATS_unspecified) {
02658     cdata->_auto_texture_scale = options.get_auto_texture_scale();
02659   }
02660 
02661   bool header_only = ((options.get_texture_flags() & (LoaderOptions::TF_preload | LoaderOptions::TF_preload_simple)) == 0);
02662   if (record != (BamCacheRecord *)NULL) {
02663     header_only = false;
02664   }
02665 
02666   if ((z == 0 || read_pages) && (n == 0 || read_mipmaps)) {
02667     // When we re-read the page 0 of the base image, we clear
02668     // everything and start over.
02669     do_clear_ram_image(cdata);
02670   }
02671 
02672   if (is_txo_filename(fullpath)) {
02673     if (record != (BamCacheRecord *)NULL) {
02674       record->add_dependent_file(fullpath);
02675     }
02676     return do_read_txo_file(cdata, fullpath);
02677   }
02678 
02679   if (is_dds_filename(fullpath)) {
02680     if (record != (BamCacheRecord *)NULL) {
02681       record->add_dependent_file(fullpath);
02682     }
02683     return do_read_dds_file(cdata, fullpath, header_only);
02684   }
02685 
02686   // If read_pages or read_mipmaps is specified, then z and n actually
02687   // indicate z_size and n_size, respectively--the numerical limits on
02688   // which to search for filenames.
02689   int z_size = z;
02690   int n_size = n;
02691 
02692   // Certain texture types have an implicit z_size.  If z_size is
02693   // omitted, choose an appropriate default based on the texture
02694   // type.
02695   if (z_size == 0) {
02696     switch (cdata->_texture_type) {
02697     case TT_1d_texture:
02698     case TT_2d_texture:
02699       z_size = 1;
02700       break;
02701 
02702     case TT_cube_map:
02703       z_size = 6;
02704       break;
02705 
02706     default:
02707       break;
02708     }
02709   }
02710 
02711   int num_views = 0;
02712   if (options.get_texture_flags() & LoaderOptions::TF_multiview) {
02713     // We'll be loading a multiview texture.
02714     read_pages = true;
02715     if (options.get_texture_num_views() != 0) {
02716       num_views = options.get_texture_num_views();
02717       do_set_num_views(cdata, num_views);
02718     }
02719   }
02720 
02721   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
02722 
02723   if (read_pages && read_mipmaps) {
02724     // Read a sequence of pages * mipmap levels.
02725     Filename fullpath_pattern = Filename::pattern_filename(fullpath);
02726     Filename alpha_fullpath_pattern = Filename::pattern_filename(alpha_fullpath);
02727     do_set_z_size(cdata, z_size);
02728 
02729     n = 0;
02730     while (true) {
02731       // For mipmap level 0, the total number of pages might be
02732       // determined by the number of files we find.  After mipmap
02733       // level 0, though, the number of pages is predetermined.
02734       if (n != 0) {
02735         z_size = do_get_expected_mipmap_z_size(cdata, n);
02736       }
02737 
02738       z = 0;
02739 
02740       Filename n_pattern = Filename::pattern_filename(fullpath_pattern.get_filename_index(z));
02741       Filename alpha_n_pattern = Filename::pattern_filename(alpha_fullpath_pattern.get_filename_index(z));
02742 
02743       if (!n_pattern.has_hash()) {
02744         gobj_cat.error()
02745           << "Filename requires two different hash sequences: " << fullpath
02746           << "\n";
02747         return false;
02748       }
02749 
02750       Filename file = n_pattern.get_filename_index(n);
02751       Filename alpha_file = alpha_n_pattern.get_filename_index(n);
02752 
02753       if ((n_size == 0 && (vfs->exists(file) || n == 0)) ||
02754           (n_size != 0 && n < n_size)) {
02755         // Continue through the loop.
02756       } else {
02757         // We've reached the end of the mipmap sequence.
02758         break;
02759       }
02760 
02761       int num_pages = z_size * num_views;
02762       while ((num_pages == 0 && (vfs->exists(file) || z == 0)) ||
02763              (num_pages != 0 && z < num_pages)) {
02764         if (!do_read_one(cdata, file, alpha_file, z, n, primary_file_num_channels,
02765                          alpha_file_channel, options, header_only, record)) {
02766           return false;
02767         }
02768         ++z;
02769 
02770         n_pattern = Filename::pattern_filename(fullpath_pattern.get_filename_index(z));
02771         file = n_pattern.get_filename_index(n);
02772         alpha_file = alpha_n_pattern.get_filename_index(n);
02773       }
02774 
02775       if (n == 0 && n_size == 0) {
02776         // If n_size is not specified, it gets implicitly set after we
02777         // read the base texture image (which determines the size of
02778         // the texture).
02779         n_size = do_get_expected_num_mipmap_levels(cdata);
02780       }
02781       ++n;
02782     }
02783     cdata->_fullpath = fullpath_pattern;
02784     cdata->_alpha_fullpath = alpha_fullpath_pattern;
02785 
02786   } else if (read_pages) {
02787     // Read a sequence of cube map or 3-D texture pages.
02788     Filename fullpath_pattern = Filename::pattern_filename(fullpath);
02789     Filename alpha_fullpath_pattern = Filename::pattern_filename(alpha_fullpath);
02790     if (!fullpath_pattern.has_hash()) {
02791       gobj_cat.error()
02792         << "Filename requires a hash mark: " << fullpath
02793         << "\n";
02794       return false;
02795     }
02796 
02797     do_set_z_size(cdata, z_size);
02798     z = 0;
02799     Filename file = fullpath_pattern.get_filename_index(z);
02800     Filename alpha_file = alpha_fullpath_pattern.get_filename_index(z);
02801 
02802     int num_pages = z_size * num_views;
02803     while ((num_pages == 0 && (vfs->exists(file) || z == 0)) ||
02804            (num_pages != 0 && z < num_pages)) {
02805       if (!do_read_one(cdata, file, alpha_file, z, 0, primary_file_num_channels,
02806                        alpha_file_channel, options, header_only, record)) {
02807         return false;
02808       }
02809       ++z;
02810 
02811       file = fullpath_pattern.get_filename_index(z);
02812       alpha_file = alpha_fullpath_pattern.get_filename_index(z);
02813     }
02814     cdata->_fullpath = fullpath_pattern;
02815     cdata->_alpha_fullpath = alpha_fullpath_pattern;
02816 
02817   } else if (read_mipmaps) {
02818     // Read a sequence of mipmap levels.
02819     Filename fullpath_pattern = Filename::pattern_filename(fullpath);
02820     Filename alpha_fullpath_pattern = Filename::pattern_filename(alpha_fullpath);
02821     if (!fullpath_pattern.has_hash()) {
02822       gobj_cat.error()
02823         << "Filename requires a hash mark: " << fullpath
02824         << "\n";
02825       return false;
02826     }
02827 
02828     n = 0;
02829     Filename file = fullpath_pattern.get_filename_index(n);
02830     Filename alpha_file = alpha_fullpath_pattern.get_filename_index(n);
02831 
02832     while ((n_size == 0 && (vfs->exists(file) || n == 0)) ||
02833            (n_size != 0 && n < n_size)) {
02834       if (!do_read_one(cdata, file, alpha_file, z, n,
02835                        primary_file_num_channels, alpha_file_channel,
02836                        options, header_only, record)) {
02837         return false;
02838       }
02839       ++n;
02840 
02841       if (n_size == 0 && n >= do_get_expected_num_mipmap_levels(cdata)) {
02842         // Don't try to read more than the requisite number of mipmap
02843         // levels (unless the user insisted on it for some reason).
02844         break;
02845       }
02846 
02847       file = fullpath_pattern.get_filename_index(n);
02848       alpha_file = alpha_fullpath_pattern.get_filename_index(n);
02849     }
02850     cdata->_fullpath = fullpath_pattern;
02851     cdata->_alpha_fullpath = alpha_fullpath_pattern;
02852 
02853   } else {
02854     // Just an ordinary read of one file.
02855     if (!do_read_one(cdata, fullpath, alpha_fullpath, z, n,
02856                      primary_file_num_channels, alpha_file_channel,
02857                      options, header_only, record)) {
02858       return false;
02859     }
02860   }
02861 
02862   cdata->_has_read_pages = read_pages;
02863   cdata->_has_read_mipmaps = read_mipmaps;
02864   cdata->_num_mipmap_levels_read = cdata->_ram_images.size();
02865 
02866   if (header_only) {
02867     // If we were only supposed to be checking the image header
02868     // information, don't let the Texture think that it's got the
02869     // image now.
02870     do_clear_ram_image(cdata);
02871   } else {
02872     if ((options.get_texture_flags() & LoaderOptions::TF_preload) != 0) {
02873       // If we intend to keep the ram image around, consider
02874       // compressing it etc.
02875       bool generate_mipmaps = ((options.get_texture_flags() & LoaderOptions::TF_generate_mipmaps) != 0);
02876       do_consider_auto_process_ram_image(cdata, generate_mipmaps || uses_mipmaps(), true);
02877     }
02878   }
02879 
02880   return true;
02881 }
02882 
02883 ////////////////////////////////////////////////////////////////////
02884 //     Function: Texture::do_read_one
02885 //       Access: Protected, Virtual
02886 //  Description: Called only from do_read(), this method reads a
02887 //               single image file, either one page or one mipmap
02888 //               level.
02889 ////////////////////////////////////////////////////////////////////
02890 bool Texture::
02891 do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpath,
02892             int z, int n, int primary_file_num_channels, int alpha_file_channel,
02893             const LoaderOptions &options, bool header_only, BamCacheRecord *record) {
02894   if (record != (BamCacheRecord *)NULL) {
02895     nassertr(!header_only, false);
02896     record->add_dependent_file(fullpath);
02897   }
02898 
02899   PNMImage image;
02900   if (header_only || textures_header_only) {
02901     if (!image.read_header(fullpath)) {
02902       gobj_cat.error()
02903         << "Texture::read() - couldn't read: " << fullpath << endl;
02904       return false;
02905     }
02906     int x_size = image.get_x_size();
02907     int y_size = image.get_y_size();
02908     if (z == 0 && n == 0) {
02909       cdata->_orig_file_x_size = x_size;
02910       cdata->_orig_file_y_size = y_size;
02911     }
02912 
02913     if (textures_header_only) {
02914       // In this mode, we never intend to load the actual texture
02915       // image anyway, so we don't even need to make the size right.
02916       x_size = 1;
02917       y_size = 1;
02918 
02919     } else {
02920       consider_rescale(image, fullpath.get_basename(), do_get_auto_texture_scale(cdata));
02921       x_size = image.get_read_x_size();
02922       y_size = image.get_read_y_size();
02923     }
02924 
02925     image = PNMImage(x_size, y_size, image.get_num_channels(),
02926                      image.get_maxval(), image.get_type());
02927     image.fill(0.2, 0.3, 1.0);
02928     if (image.has_alpha()) {
02929       image.alpha_fill(1.0);
02930     }
02931 
02932   } else {
02933     if (!image.read_header(fullpath, NULL, false)) {
02934       gobj_cat.error()
02935         << "Texture::read() - couldn't read: " << fullpath << endl;
02936       return false;
02937     }
02938 
02939     if (z == 0 && n == 0) {
02940       cdata->_orig_file_x_size = image.get_x_size();
02941       cdata->_orig_file_y_size = image.get_y_size();
02942       consider_rescale(image, fullpath.get_basename(), do_get_auto_texture_scale(cdata));
02943     } else {
02944       image.set_read_size(do_get_expected_mipmap_x_size(cdata, n),
02945                           do_get_expected_mipmap_y_size(cdata, n));
02946     }
02947 
02948     if (image.get_x_size() != image.get_read_x_size() ||
02949         image.get_y_size() != image.get_read_y_size()) {
02950       gobj_cat.info()
02951         << "Implicitly rescaling " << fullpath.get_basename() << " from "
02952         << image.get_x_size() << " by " << image.get_y_size() << " to "
02953         << image.get_read_x_size() << " by " << image.get_read_y_size()
02954         << "\n";
02955     }
02956 
02957     if (!image.read(fullpath, NULL, false)) {
02958       gobj_cat.error()
02959         << "Texture::read() - couldn't read: " << fullpath << endl;
02960       return false;
02961     }
02962     Thread::consider_yield();
02963   }
02964 
02965   PNMImage alpha_image;
02966   if (!alpha_fullpath.empty()) {
02967     if (record != (BamCacheRecord *)NULL) {
02968       record->add_dependent_file(alpha_fullpath);
02969     }
02970 
02971     if (header_only || textures_header_only) {
02972       if (!alpha_image.read_header(alpha_fullpath)) {
02973         gobj_cat.error()
02974           << "Texture::read() - couldn't read: " << alpha_fullpath << endl;
02975         return false;
02976       }
02977       int x_size = image.get_x_size();
02978       int y_size = image.get_y_size();
02979       alpha_image = PNMImage(x_size, y_size, alpha_image.get_num_channels(),
02980                              alpha_image.get_maxval(), alpha_image.get_type());
02981       alpha_image.fill(1.0);
02982       if (alpha_image.has_alpha()) {
02983         alpha_image.alpha_fill(1.0);
02984       }
02985 
02986     } else {
02987       if (!alpha_image.read_header(alpha_fullpath, NULL, true)) {
02988         gobj_cat.error()
02989           << "Texture::read() - couldn't read (alpha): " << alpha_fullpath << endl;
02990         return false;
02991       }
02992 
02993       if (image.get_x_size() != alpha_image.get_x_size() ||
02994           image.get_y_size() != alpha_image.get_y_size()) {
02995         gobj_cat.info()
02996           << "Implicitly rescaling " << alpha_fullpath.get_basename()
02997           << " from " << alpha_image.get_x_size() << " by "
02998           << alpha_image.get_y_size() << " to " << image.get_x_size()
02999           << " by " << image.get_y_size() << "\n";
03000         alpha_image.set_read_size(image.get_x_size(), image.get_y_size());
03001       }
03002 
03003       if (!alpha_image.read(alpha_fullpath, NULL, true)) {
03004         gobj_cat.error()
03005           << "Texture::read() - couldn't read (alpha): " << alpha_fullpath << endl;
03006         return false;
03007       }
03008       Thread::consider_yield();
03009     }
03010   }
03011 
03012   if (z == 0 && n == 0) {
03013     if (!has_name()) {
03014       set_name(fullpath.get_basename_wo_extension());
03015     }
03016     if (cdata->_filename.empty()) {
03017       cdata->_filename = fullpath;
03018       cdata->_alpha_filename = alpha_fullpath;
03019 
03020       // The first time we set the filename via a read() operation, we
03021       // clear keep_ram_image.  The user can always set it again later
03022       // if he needs to.
03023       cdata->_keep_ram_image = false;
03024     }
03025 
03026     cdata->_fullpath = fullpath;
03027     cdata->_alpha_fullpath = alpha_fullpath;
03028   }
03029 
03030   if (!alpha_fullpath.empty()) {
03031     // The grayscale (alpha channel) image must be the same size as
03032     // the main image.  This should really have been already
03033     // guaranteed by the above.
03034     if (image.get_x_size() != alpha_image.get_x_size() ||
03035         image.get_y_size() != alpha_image.get_y_size()) {
03036       gobj_cat.info()
03037         << "Automatically rescaling " << alpha_fullpath.get_basename()
03038         << " from " << alpha_image.get_x_size() << " by "
03039         << alpha_image.get_y_size() << " to " << image.get_x_size()
03040         << " by " << image.get_y_size() << "\n";
03041 
03042       PNMImage scaled(image.get_x_size(), image.get_y_size(),
03043                       alpha_image.get_num_channels(),
03044                       alpha_image.get_maxval(), alpha_image.get_type());
03045       scaled.quick_filter_from(alpha_image);
03046       Thread::consider_yield();
03047       alpha_image = scaled;
03048     }
03049   }
03050 
03051   if (n == 0) {
03052     consider_downgrade(image, primary_file_num_channels, get_name());
03053     cdata->_primary_file_num_channels = image.get_num_channels();
03054     cdata->_alpha_file_channel = 0;
03055   }
03056 
03057   if (!alpha_fullpath.empty()) {
03058     // Make the original image a 4-component image by taking the
03059     // grayscale value from the second image.
03060     image.add_alpha();
03061 
03062     if (alpha_file_channel == 4 ||
03063         (alpha_file_channel == 2 && alpha_image.get_num_channels() == 2)) {
03064       // Use the alpha channel.
03065       for (int x = 0; x < image.get_x_size(); x++) {
03066         for (int y = 0; y < image.get_y_size(); y++) {
03067           image.set_alpha(x, y, alpha_image.get_alpha(x, y));
03068         }
03069       }
03070       cdata->_alpha_file_channel = alpha_image.get_num_channels();
03071 
03072     } else if (alpha_file_channel >= 1 && alpha_file_channel <= 3 &&
03073                alpha_image.get_num_channels() >= 3) {
03074       // Use the appropriate red, green, or blue channel.
03075       for (int x = 0; x < image.get_x_size(); x++) {
03076         for (int y = 0; y < image.get_y_size(); y++) {
03077           image.set_alpha(x, y, alpha_image.get_channel_val(x, y, alpha_file_channel - 1));
03078         }
03079       }
03080       cdata->_alpha_file_channel = alpha_file_channel;
03081 
03082     } else {
03083       // Use the grayscale channel.
03084       for (int x = 0; x < image.get_x_size(); x++) {
03085         for (int y = 0; y < image.get_y_size(); y++) {
03086           image.set_alpha(x, y, alpha_image.get_gray(x, y));
03087         }
03088       }
03089       cdata->_alpha_file_channel = 0;
03090     }
03091   }
03092 
03093   // Now see if we want to pad the image within a larger power-of-2
03094   // image.
03095   int pad_x_size = 0;
03096   int pad_y_size = 0;
03097   if (do_get_auto_texture_scale(cdata) == ATS_pad) {
03098     int new_x_size = image.get_x_size();
03099     int new_y_size = image.get_y_size();
03100     if (do_adjust_this_size(cdata, new_x_size, new_y_size, fullpath.get_basename(), true)) {
03101       pad_x_size = new_x_size - image.get_x_size();
03102       pad_y_size = new_y_size - image.get_y_size();
03103       PNMImage new_image(new_x_size, new_y_size, image.get_num_channels(),
03104                          image.get_maxval());
03105       new_image.copy_sub_image(image, 0, new_y_size - image.get_y_size());
03106       image.take_from(new_image);
03107     }
03108   }
03109 
03110   if (!do_load_one(cdata, image, fullpath.get_basename(), z, n, options)) {
03111     return false;
03112   }
03113 
03114   do_set_pad_size(cdata, pad_x_size, pad_y_size, 0);
03115   return true;
03116 }
03117 
03118 ////////////////////////////////////////////////////////////////////
03119 //     Function: Texture::do_load_one
03120 //       Access: Protected, Virtual
03121 //  Description: Internal method to load a single page or mipmap
03122 //               level.
03123 ////////////////////////////////////////////////////////////////////
03124 bool Texture::
03125 do_load_one(CData *cdata, const PNMImage &pnmimage, const string &name, int z, int n,
03126             const LoaderOptions &options) {
03127   if (cdata->_ram_images.size() <= 1 && n == 0) {
03128     // A special case for mipmap level 0.  When we load mipmap level
03129     // 0, unless we already have mipmap levels, it determines the
03130     // image properties like size and number of components.
03131     if (!do_reconsider_z_size(cdata, z, options)) {
03132       return false;
03133     }
03134     nassertr(z >= 0 && z < cdata->_z_size * cdata->_num_views, false);
03135 
03136     if (z == 0) {
03137       ComponentType component_type = T_unsigned_byte;
03138       xelval maxval = pnmimage.get_maxval();
03139       if (maxval > 255) {
03140         component_type = T_unsigned_short;
03141       }
03142 
03143       if (!do_reconsider_image_properties(cdata, pnmimage.get_x_size(), pnmimage.get_y_size(),
03144                                           pnmimage.get_num_channels(), component_type,
03145                                           z, options)) {
03146         return false;
03147       }
03148     }
03149 
03150     do_modify_ram_image(cdata);
03151     cdata->_loaded_from_image = true;
03152   }
03153 
03154   do_modify_ram_mipmap_image(cdata, n);
03155 
03156   // Ensure the PNMImage is an appropriate size.
03157   int x_size = do_get_expected_mipmap_x_size(cdata, n);
03158   int y_size = do_get_expected_mipmap_y_size(cdata, n);
03159   if (pnmimage.get_x_size() != x_size ||
03160       pnmimage.get_y_size() != y_size) {
03161     gobj_cat.info()
03162       << "Automatically rescaling " << name;
03163     if (n != 0) {
03164       gobj_cat.info(false)
03165         << " mipmap level " << n;
03166     }
03167     gobj_cat.info(false)
03168       << " from " << pnmimage.get_x_size() << " by "
03169       << pnmimage.get_y_size() << " to " << x_size << " by "
03170       << y_size << "\n";
03171 
03172     PNMImage scaled(x_size, y_size, pnmimage.get_num_channels(),
03173                     pnmimage.get_maxval(), pnmimage.get_type());
03174     scaled.quick_filter_from(pnmimage);
03175     Thread::consider_yield();
03176 
03177     convert_from_pnmimage(cdata->_ram_images[n]._image,
03178                           do_get_expected_ram_mipmap_page_size(cdata, n), z,
03179                           scaled, cdata->_num_components, cdata->_component_width);
03180   } else {
03181     // Now copy the pixel data from the PNMImage into our internal
03182     // cdata->_image component.
03183     convert_from_pnmimage(cdata->_ram_images[n]._image,
03184                           do_get_expected_ram_mipmap_page_size(cdata, n), z,
03185                           pnmimage, cdata->_num_components, cdata->_component_width);
03186   }
03187   Thread::consider_yield();
03188 
03189   return true;
03190 }
03191 
03192 ////////////////////////////////////////////////////////////////////
03193 //     Function: Texture::do_read_txo_file
03194 //       Access: Protected
03195 //  Description: Called internally when read() detects a txo file.
03196 //               Assumes the lock is already held.
03197 ////////////////////////////////////////////////////////////////////
03198 bool Texture::
03199 do_read_txo_file(CData *cdata, const Filename &fullpath) {
03200   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
03201 
03202   Filename filename = Filename::binary_filename(fullpath);
03203   PT(VirtualFile) file = vfs->get_file(filename);
03204   if (file == (VirtualFile *)NULL) {
03205     // No such file.
03206     gobj_cat.error()
03207       << "Could not find " << fullpath << "\n";
03208     return false;
03209   }
03210 
03211   if (gobj_cat.is_debug()) {
03212     gobj_cat.debug()
03213       << "Reading texture object " << filename << "\n";
03214   }
03215 
03216   istream *in = file->open_read_file(true);
03217   bool success = do_read_txo(cdata, *in, fullpath);
03218   vfs->close_read_file(in);
03219 
03220   cdata->_fullpath = fullpath;
03221   cdata->_alpha_fullpath = Filename();
03222   cdata->_keep_ram_image = false;
03223 
03224   return success;
03225 }
03226 
03227 ////////////////////////////////////////////////////////////////////
03228 //     Function: Texture::do_read_txo
03229 //       Access: Protected
03230 //  Description:
03231 ////////////////////////////////////////////////////////////////////
03232 bool Texture::
03233 do_read_txo(CData *cdata, istream &in, const string &filename) {
03234   PT(Texture) other = make_from_txo(in, filename);
03235   if (other == (Texture *)NULL) {
03236     return false;
03237   }
03238 
03239   CDReader cdata_other(other->_cycler);
03240   Namable::operator = (*other);
03241   do_assign(cdata, other, cdata_other);
03242 
03243   cdata->_loaded_from_image = true;
03244   cdata->_loaded_from_txo = true;
03245   cdata->_has_read_pages = false;
03246   cdata->_has_read_mipmaps = false;
03247   cdata->_num_mipmap_levels_read = 0;
03248   return true;
03249 }
03250 
03251 ////////////////////////////////////////////////////////////////////
03252 //     Function: Texture::do_read_dds_file
03253 //       Access: Private
03254 //  Description: Called internally when read() detects a DDS file.
03255 //               Assumes the lock is already held.
03256 ////////////////////////////////////////////////////////////////////
03257 bool Texture::
03258 do_read_dds_file(CData *cdata, const Filename &fullpath, bool header_only) {
03259   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
03260 
03261   Filename filename = Filename::binary_filename(fullpath);
03262   PT(VirtualFile) file = vfs->get_file(filename);
03263   if (file == (VirtualFile *)NULL) {
03264     // No such file.
03265     gobj_cat.error()
03266       << "Could not find " << fullpath << "\n";
03267     return false;
03268   }
03269 
03270   if (gobj_cat.is_debug()) {
03271     gobj_cat.debug()
03272       << "Reading DDS file " << filename << "\n";
03273   }
03274 
03275   istream *in = file->open_read_file(true);
03276   bool success = do_read_dds(cdata, *in, fullpath, header_only);
03277   vfs->close_read_file(in);
03278 
03279   if (!has_name()) {
03280     set_name(fullpath.get_basename_wo_extension());
03281   }
03282 
03283   cdata->_fullpath = fullpath;
03284   cdata->_alpha_fullpath = Filename();
03285   cdata->_keep_ram_image = false;
03286 
03287   return success;
03288 }
03289 
03290 ////////////////////////////////////////////////////////////////////
03291 //     Function: Texture::do_read_dds
03292 //       Access: Protected
03293 //  Description:
03294 ////////////////////////////////////////////////////////////////////
03295 bool Texture::
03296 do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) {
03297   StreamReader dds(in);
03298 
03299   // DDS header (19 words)
03300   DDSHeader header;
03301   header.dds_magic = dds.get_uint32();
03302   header.dds_size = dds.get_uint32();
03303   header.dds_flags = dds.get_uint32();
03304   header.height = dds.get_uint32();
03305   header.width = dds.get_uint32();
03306   header.pitch = dds.get_uint32();
03307   header.depth = dds.get_uint32();
03308   header.num_levels = dds.get_uint32();
03309   dds.skip_bytes(44);
03310 
03311   // Pixelformat (8 words)
03312   header.pf.pf_size = dds.get_uint32();
03313   header.pf.pf_flags = dds.get_uint32();
03314   header.pf.four_cc = dds.get_uint32();
03315   header.pf.rgb_bitcount = dds.get_uint32();
03316   header.pf.r_mask = dds.get_uint32();
03317   header.pf.g_mask = dds.get_uint32();
03318   header.pf.b_mask = dds.get_uint32();
03319   header.pf.a_mask = dds.get_uint32();
03320 
03321   // Caps (4 words)
03322   header.caps.caps1 = dds.get_uint32();
03323   header.caps.caps2 = dds.get_uint32();
03324   header.caps.ddsx = dds.get_uint32();
03325   dds.skip_bytes(4);
03326 
03327   // Pad out to 32 words
03328   dds.skip_bytes(4);
03329 
03330   if (header.dds_magic != DDS_MAGIC || (in.fail() || in.eof())) {
03331     gobj_cat.error()
03332       << filename << " is not a DDS file.\n";
03333     return false;
03334   }
03335 
03336   if ((header.dds_flags & DDSD_MIPMAPCOUNT) == 0) {
03337     // No bit set means only the base mipmap level.
03338     header.num_levels = 1;
03339   }
03340 
03341   TextureType texture_type;
03342   if (header.caps.caps2 & DDSCAPS2_CUBEMAP) {
03343     static const unsigned int all_faces =
03344       (DDSCAPS2_CUBEMAP_POSITIVEX |
03345        DDSCAPS2_CUBEMAP_POSITIVEY |
03346        DDSCAPS2_CUBEMAP_POSITIVEZ |
03347        DDSCAPS2_CUBEMAP_NEGATIVEX |
03348        DDSCAPS2_CUBEMAP_NEGATIVEY |
03349        DDSCAPS2_CUBEMAP_NEGATIVEZ);
03350     if ((header.caps.caps2 & all_faces) != all_faces) {
03351       gobj_cat.error()
03352         << filename << " is missing some cube map faces; cannot load.\n";
03353       return false;
03354     }
03355     header.depth = 6;
03356     texture_type = TT_cube_map;
03357 
03358   } else if (header.caps.caps2 & DDSCAPS2_VOLUME) {
03359     texture_type = TT_3d_texture;
03360 
03361   } else {
03362     texture_type = TT_2d_texture;
03363     header.depth = 1;
03364   }
03365 
03366   // Determine the function to use to read the DDS image.
03367   typedef PTA_uchar (*ReadDDSLevelFunc)(Texture *tex, Texture::CData *cdata,
03368                                         const DDSHeader &header, int n, istream &in);
03369   ReadDDSLevelFunc func = NULL;
03370 
03371   Format format = F_rgb;
03372 
03373   do_clear_ram_image(cdata);
03374   CompressionMode compression = CM_off;
03375 
03376   if (header.pf.pf_flags & DDPF_FOURCC) {
03377     // Some compressed texture format.
03378     if (texture_type == TT_3d_texture) {
03379       gobj_cat.error()
03380         << filename << ": unsupported compression on 3-d texture.\n";
03381       return false;
03382     }
03383 
03384     if (header.pf.four_cc == 0x31545844) {   // 'DXT1', little-endian.
03385       compression = CM_dxt1;
03386       func = read_dds_level_dxt1;
03387     } else if (header.pf.four_cc == 0x32545844) {   // 'DXT2'
03388       compression = CM_dxt2;
03389       func = read_dds_level_dxt23;
03390     } else if (header.pf.four_cc == 0x33545844) {   // 'DXT3'
03391       compression = CM_dxt3;
03392       func = read_dds_level_dxt23;
03393     } else if (header.pf.four_cc == 0x34545844) {   // 'DXT4'
03394       compression = CM_dxt4;
03395       func = read_dds_level_dxt45;
03396     } else if (header.pf.four_cc == 0x35545844) {   // 'DXT5'
03397       compression = CM_dxt5;
03398       func = read_dds_level_dxt45;
03399     } else {
03400       gobj_cat.error()
03401         << filename << ": unsupported texture compression.\n";
03402       return false;
03403     }
03404 
03405     // All of the compressed formats support alpha, even DXT1 (to some
03406     // extent, at least).
03407     format = F_rgba;
03408 
03409   } else {
03410     // An uncompressed texture format.
03411     func = read_dds_level_generic_uncompressed;
03412 
03413     if (header.pf.pf_flags & DDPF_ALPHAPIXELS) {
03414       // An uncompressed format that involves alpha.
03415       format = F_rgba;
03416       if (header.pf.rgb_bitcount == 32 &&
03417           header.pf.r_mask == 0x000000ff &&
03418           header.pf.g_mask == 0x0000ff00 &&
03419           header.pf.b_mask == 0x00ff0000 &&
03420           header.pf.a_mask == 0xff000000U) {
03421         func = read_dds_level_abgr8;
03422       } else if (header.pf.rgb_bitcount == 32 &&
03423           header.pf.r_mask == 0x00ff0000 &&
03424           header.pf.g_mask == 0x0000ff00 &&
03425           header.pf.b_mask == 0x000000ff &&
03426           header.pf.a_mask == 0xff000000U) {
03427         func = read_dds_level_rgba8;
03428 
03429       } else if (header.pf.r_mask != 0 &&
03430                  header.pf.g_mask == 0 &&
03431                  header.pf.b_mask == 0) {
03432         func = read_dds_level_luminance_uncompressed;
03433         format = F_luminance_alpha;
03434       }
03435     } else {
03436       // An uncompressed format that doesn't involve alpha.
03437       if (header.pf.rgb_bitcount == 24 &&
03438           header.pf.r_mask == 0x00ff0000 &&
03439           header.pf.g_mask == 0x0000ff00 &&
03440           header.pf.b_mask == 0x000000ff) {
03441         func = read_dds_level_bgr8;
03442       } else if (header.pf.rgb_bitcount == 24 &&
03443                  header.pf.r_mask == 0x000000ff &&
03444                  header.pf.g_mask == 0x0000ff00 &&
03445                  header.pf.b_mask == 0x00ff0000) {
03446         func = read_dds_level_rgb8;
03447 
03448       } else if (header.pf.r_mask != 0 &&
03449                  header.pf.g_mask == 0 &&
03450                  header.pf.b_mask == 0) {
03451         func = read_dds_level_luminance_uncompressed;
03452         format = F_luminance;
03453       }
03454     }
03455 
03456   }
03457 
03458   do_setup_texture(cdata, texture_type, header.width, header.height, header.depth,
03459                    T_unsigned_byte, format);
03460 
03461   cdata->_orig_file_x_size = cdata->_x_size;
03462   cdata->_orig_file_y_size = cdata->_y_size;
03463   cdata->_compression = compression;
03464   cdata->_ram_image_compression = compression;
03465 
03466   if (!header_only) {
03467     switch (texture_type) {
03468     case TT_3d_texture:
03469       {
03470         // 3-d textures store all the depth slices for mipmap level 0,
03471         // then all the depth slices for mipmap level 1, and so on.
03472         for (int n = 0; n < (int)header.num_levels; ++n) {
03473           int z_size = do_get_expected_mipmap_z_size(cdata, n);
03474           pvector<PTA_uchar> pages;
03475           size_t page_size = 0;
03476           int z;
03477           for (z = 0; z < z_size; ++z) {
03478             PTA_uchar page = func(this, cdata, header, n, in);
03479             if (page.is_null()) {
03480               return false;
03481             }
03482             nassertr(page_size == 0 || page_size == page.size(), false);
03483             page_size = page.size();
03484             pages.push_back(page);
03485           }
03486           // Now reassemble the pages into one big image.  Because
03487           // this is a Microsoft format, the images are stacked in
03488           // reverse order; re-reverse them.
03489           PTA_uchar image = PTA_uchar::empty_array(page_size * z_size);
03490           unsigned char *imagep = (unsigned char *)image.p();
03491           for (z = 0; z < z_size; ++z) {
03492             int fz = z_size - 1 - z;
03493             memcpy(imagep + z * page_size, pages[fz].p(), page_size);
03494           }
03495 
03496           do_set_ram_mipmap_image(cdata, n, image, page_size);
03497         }
03498       }
03499       break;
03500 
03501     case TT_cube_map:
03502       {
03503         // Cube maps store all the mipmap levels for face 0, then all
03504         // the mipmap levels for face 1, and so on.
03505         pvector<pvector<PTA_uchar> > pages;
03506         pages.reserve(6);
03507         int z, n;
03508         for (z = 0; z < 6; ++z) {
03509           pages.push_back(pvector<PTA_uchar>());
03510           pvector<PTA_uchar> &levels = pages.back();
03511           levels.reserve(header.num_levels);
03512 
03513           for (n = 0; n < (int)header.num_levels; ++n) {
03514             PTA_uchar image = func(this, cdata, header, n, in);
03515             if (image.is_null()) {
03516               return false;
03517             }
03518             levels.push_back(image);
03519           }
03520         }
03521 
03522         // Now, for each level, reassemble the pages into one big
03523         // image.  Because this is a Microsoft format, the levels are
03524         // arranged in a rotated order.
03525         static const int level_remap[6] = {
03526           0, 1, 5, 4, 2, 3
03527         };
03528         for (n = 0; n < (int)header.num_levels; ++n) {
03529           size_t page_size = pages[0][n].size();
03530           PTA_uchar image = PTA_uchar::empty_array(page_size * 6);
03531           unsigned char *imagep = (unsigned char *)image.p();
03532           for (z = 0; z < 6; ++z) {
03533             int fz = level_remap[z];
03534             nassertr(pages[fz][n].size() == page_size, false);
03535             memcpy(imagep + z * page_size, pages[fz][n].p(), page_size);
03536           }
03537 
03538           do_set_ram_mipmap_image(cdata, n, image, page_size);
03539         }
03540       }
03541       break;
03542 
03543     default:
03544       // Normal 2-d textures simply store the mipmap levels.
03545       {
03546         for (int n = 0; n < (int)header.num_levels; ++n) {
03547           PTA_uchar image = func(this, cdata, header, n, in);
03548           if (image.is_null()) {
03549             return false;
03550           }
03551           do_set_ram_mipmap_image(cdata, n, image, 0);
03552         }
03553       }
03554     }
03555     cdata->_has_read_pages = true;
03556     cdata->_has_read_mipmaps = true;
03557     cdata->_num_mipmap_levels_read = cdata->_ram_images.size();
03558   }
03559 
03560   if (in.fail() || in.eof()) {
03561     gobj_cat.error()
03562       << filename << ": truncated DDS file.\n";
03563     return false;
03564   }
03565 
03566   cdata->_loaded_from_image = true;
03567   cdata->_loaded_from_txo = true;
03568 
03569   return true;
03570 }
03571 
03572 ////////////////////////////////////////////////////////////////////
03573 //     Function: Texture::do_write
03574 //       Access: Protected
03575 //  Description: Internal method to write a series of pages and/or
03576 //               mipmap levels to disk files.
03577 ////////////////////////////////////////////////////////////////////
03578 bool Texture::
03579 do_write(CData *cdata, 
03580          const Filename &fullpath, int z, int n, bool write_pages, bool write_mipmaps) {
03581   if (is_txo_filename(fullpath)) {
03582     if (!do_has_bam_rawdata(cdata)) {
03583       do_get_bam_rawdata(cdata);
03584     }
03585     nassertr(do_has_bam_rawdata(cdata), false);
03586     return do_write_txo_file(cdata, fullpath);
03587   }
03588 
03589   if (!do_has_uncompressed_ram_image(cdata)) {
03590     do_get_uncompressed_ram_image(cdata);
03591   }
03592 
03593   nassertr(do_has_ram_mipmap_image(cdata, n), false);
03594   nassertr(cdata->_ram_image_compression == CM_off, false);
03595 
03596   if (write_pages && write_mipmaps) {
03597     // Write a sequence of pages * mipmap levels.
03598     Filename fullpath_pattern = Filename::pattern_filename(fullpath);
03599     int num_levels = cdata->_ram_images.size();
03600 
03601     for (int n = 0; n < num_levels; ++n) {
03602       int num_pages = do_get_expected_mipmap_num_pages(cdata, n);
03603 
03604       for (z = 0; z < num_pages; ++z) {
03605         Filename n_pattern = Filename::pattern_filename(fullpath_pattern.get_filename_index(z));
03606 
03607         if (!n_pattern.has_hash()) {
03608           gobj_cat.error()
03609             << "Filename requires two different hash sequences: " << fullpath
03610             << "\n";
03611           return false;
03612         }
03613 
03614         if (!do_write_one(cdata, n_pattern.get_filename_index(n), z, n)) {
03615           return false;
03616         }
03617       }
03618     }
03619 
03620   } else if (write_pages) {
03621     // Write a sequence of pages.
03622     Filename fullpath_pattern = Filename::pattern_filename(fullpath);
03623     if (!fullpath_pattern.has_hash()) {
03624       gobj_cat.error()
03625         << "Filename requires a hash mark: " << fullpath
03626         << "\n";
03627       return false;
03628     }
03629 
03630     int num_pages = cdata->_z_size * cdata->_num_views;
03631     for (z = 0; z < num_pages; ++z) {
03632       if (!do_write_one(cdata, fullpath_pattern.get_filename_index(z), z, n)) {
03633         return false;
03634       }
03635     }
03636 
03637   } else if (write_mipmaps) {
03638     // Write a sequence of mipmap images.
03639     Filename fullpath_pattern = Filename::pattern_filename(fullpath);
03640     if (!fullpath_pattern.has_hash()) {
03641       gobj_cat.error()
03642         << "Filename requires a hash mark: " << fullpath
03643         << "\n";
03644       return false;
03645     }
03646 
03647     int num_levels = cdata->_ram_images.size();
03648     for (int n = 0; n < num_levels; ++n) {
03649       if (!do_write_one(cdata, fullpath_pattern.get_filename_index(n), z, n)) {
03650         return false;
03651       }
03652     }
03653 
03654   } else {
03655     // Write a single file.
03656     if (!do_write_one(cdata, fullpath, z, n)) {
03657       return false;
03658     }
03659   }
03660 
03661   return true;
03662 }
03663 
03664 ////////////////////////////////////////////////////////////////////
03665 //     Function: Texture::do_write_one
03666 //       Access: Protected
03667 //  Description: Internal method to write the indicated page and
03668 //               mipmap level to a disk image file.
03669 ////////////////////////////////////////////////////////////////////
03670 bool Texture::
03671 do_write_one(CData *cdata, const Filename &fullpath, int z, int n) {
03672   if (!do_has_ram_mipmap_image(cdata, n)) {
03673     return false;
03674   }
03675 
03676   nassertr(cdata->_ram_image_compression == CM_off, false);
03677 
03678   PNMImage pnmimage;
03679   if (!do_store_one(cdata, pnmimage, z, n)) {
03680     return false;
03681   }
03682 
03683   if (!pnmimage.write(fullpath)) {
03684     gobj_cat.error()
03685       << "Texture::write() - couldn't write: " << fullpath << endl;
03686     return false;
03687   }
03688   return true;
03689 }
03690 
03691 ////////////////////////////////////////////////////////////////////
03692 //     Function: Texture::do_store_one
03693 //       Access: Protected
03694 //  Description: Internal method to copy a page and/or mipmap level to
03695 //               a PNMImage.
03696 ////////////////////////////////////////////////////////////////////
03697 bool Texture::
03698 do_store_one(CData *cdata, PNMImage &pnmimage, int z, int n) {
03699   // First, reload the ram image if necessary.
03700   do_get_uncompressed_ram_image(cdata);
03701 
03702   if (!do_has_ram_mipmap_image(cdata, n)) {
03703     return false;
03704   }
03705 
03706   nassertr(z >= 0 && z < do_get_expected_mipmap_num_pages(cdata, n), false);
03707   nassertr(cdata->_ram_image_compression == CM_off, false);
03708 
03709   return convert_to_pnmimage(pnmimage,
03710                              do_get_expected_mipmap_x_size(cdata, n),
03711                              do_get_expected_mipmap_y_size(cdata, n),
03712                              cdata->_num_components, cdata->_component_width,
03713                              cdata->_ram_images[n]._image,
03714                              do_get_ram_mipmap_page_size(cdata, n), z);
03715 }
03716 
03717 ////////////////////////////////////////////////////////////////////
03718 //     Function: Texture::do_write_txo_file
03719 //       Access: Private
03720 //  Description: Called internally when write() detects a txo
03721 //               filename.
03722 ////////////////////////////////////////////////////////////////////
03723 bool Texture::
03724 do_write_txo_file(const CData *cdata, const Filename &fullpath) const {
03725   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
03726   Filename filename = Filename::binary_filename(fullpath);
03727   ostream *out = vfs->open_write_file(filename, true, true);
03728   if (out == NULL) {
03729     gobj_cat.error()
03730       << "Unable to open " << filename << "\n";
03731     return false;
03732   }
03733 
03734   bool success = do_write_txo(cdata, *out, fullpath);
03735   vfs->close_write_file(out);
03736   return success;
03737 }
03738 
03739 ////////////////////////////////////////////////////////////////////
03740 //     Function: Texture::do_write_txo
03741 //       Access: Protected
03742 //  Description:
03743 ////////////////////////////////////////////////////////////////////
03744 bool Texture::
03745 do_write_txo(const CData *cdata, ostream &out, const string &filename) const {
03746   DatagramOutputFile dout;
03747 
03748   if (!dout.open(out, filename)) {
03749     gobj_cat.error()
03750       << "Could not write texture object: " << filename << "\n";
03751     return false;
03752   }
03753 
03754   if (!dout.write_header(_bam_header)) {
03755     gobj_cat.error()
03756       << "Unable to write to " << filename << "\n";
03757     return false;
03758   }
03759 
03760   BamWriter writer(&dout);
03761   if (!writer.init()) {
03762     return false;
03763   }
03764 
03765   writer.set_file_texture_mode(BamWriter::BTM_rawdata);
03766 
03767   if (!writer.write_object(this)) {
03768     return false;
03769   }
03770 
03771   if (!do_has_bam_rawdata(cdata)) {
03772     gobj_cat.error()
03773       << get_name() << " does not have ram image\n";
03774     return false;
03775   }
03776 
03777   return true;
03778 }
03779 
03780 ////////////////////////////////////////////////////////////////////
03781 //     Function: Texture::unlocked_ensure_ram_image
03782 //       Access: Protected, Virtual
03783 //  Description: If the texture has a ram image already, this acquires
03784 //               the CData write lock and returns it.
03785 //
03786 //               If the texture lacks a ram image, this performs
03787 //               do_reload_ram_image(), but without holding the lock
03788 //               on this particular Texture object, to avoid holding
03789 //               the lock across what might be a slow operation.
03790 //               Instead, the reload is performed in a copy of the
03791 //               texture object, and then the lock is acquired and the
03792 //               data is copied in.
03793 //
03794 //               In any case, the return value is a locked CData
03795 //               object, which must be released with an explicit call
03796 //               to release_write().  The CData object will have a ram
03797 //               image unless for some reason do_reload_ram_image()
03798 //               fails.
03799 ////////////////////////////////////////////////////////////////////
03800 Texture::CData *Texture::
03801 unlocked_ensure_ram_image(bool allow_compression) {
03802   Thread *current_thread = Thread::get_current_thread();
03803 
03804   // First, wait for any other threads that might be simultaneously
03805   // performing the same operation.
03806   MutexHolder holder(_lock);
03807   while (_reloading) {
03808     _cvar.wait();
03809   }
03810 
03811   // Then make sure we still need to reload before continuing.
03812   const CData *cdata = _cycler.read(current_thread);
03813   bool has_ram_image = do_has_ram_image(cdata);
03814   if (has_ram_image && !allow_compression && cdata->_ram_image_compression != Texture::CM_off) {
03815     // If we don't want compression, but the ram image we have is
03816     // pre-compressed, we don't consider it.
03817     has_ram_image = false;
03818   }
03819   if (has_ram_image || !do_can_reload(cdata)) {
03820     // We don't need to reload after all, or maybe we can't reload
03821     // anyway.  Return, but elevate the lock first, as we promised.
03822     return _cycler.elevate_read_upstream(cdata, false, current_thread);
03823   }
03824 
03825   // We need to reload.
03826   nassertr(!_reloading, NULL);
03827   _reloading = true;
03828   
03829   PT(Texture) tex = do_make_copy(cdata);
03830   _cycler.release_read(cdata);
03831   _lock.release();
03832   
03833   // Perform the actual reload in a copy of the texture, while our
03834   // own mutex is left unlocked.
03835   CDWriter cdata_tex(tex->_cycler, true);
03836   tex->do_reload_ram_image(cdata_tex, allow_compression);
03837   
03838   _lock.acquire();
03839 
03840   CData *cdataw = _cycler.write_upstream(false, current_thread);
03841   
03842   // Rather than calling do_assign(), which would copy *all* of the
03843   // reloaded texture's properties over, we only copy in the ones
03844   // which are relevant to the ram image.  This way, if the
03845   // properties have changed during the reload (for instance,
03846   // because we reloaded a txo), it won't contaminate the original
03847   // texture.
03848   cdataw->_orig_file_x_size = cdata_tex->_orig_file_x_size;
03849   cdataw->_orig_file_y_size = cdata_tex->_orig_file_y_size;
03850   
03851   // If any of *these* properties have changed, the texture has
03852   // changed in some fundamental way.  Update it appropriately.
03853   if (cdata_tex->_x_size != cdataw->_x_size ||
03854       cdata_tex->_y_size != cdataw->_y_size ||
03855       cdata_tex->_z_size != cdataw->_z_size ||
03856       cdata_tex->_num_views != cdataw->_num_views ||
03857       cdata_tex->_num_components != cdataw->_num_components ||
03858       cdata_tex->_component_width != cdataw->_component_width ||
03859       cdata_tex->_texture_type != cdataw->_texture_type ||
03860       cdata_tex->_component_type != cdataw->_component_type) {
03861     
03862     cdataw->_x_size = cdata_tex->_x_size;
03863     cdataw->_y_size = cdata_tex->_y_size;
03864     cdataw->_z_size = cdata_tex->_z_size;
03865     cdataw->_num_views = cdata_tex->_num_views;
03866     
03867     cdataw->_num_components = cdata_tex->_num_components;
03868     cdataw->_component_width = cdata_tex->_component_width;
03869     cdataw->_texture_type = cdata_tex->_texture_type;
03870     cdataw->_format = cdata_tex->_format;
03871     cdataw->_component_type = cdata_tex->_component_type;
03872     
03873     ++(cdataw->_properties_modified);
03874     ++(cdataw->_image_modified);
03875   }
03876   
03877   cdataw->_keep_ram_image = cdata_tex->_keep_ram_image;
03878   cdataw->_ram_image_compression = cdata_tex->_ram_image_compression;
03879   cdataw->_ram_images = cdata_tex->_ram_images;
03880   
03881   nassertr(_reloading, NULL);
03882   _reloading = false;
03883   
03884   // We don't generally increment the cdata->_image_modified semaphore,
03885   // because this is just a reload, and presumably the image hasn't
03886   // changed (unless we hit the if condition above).
03887   
03888   _cvar.notify_all();
03889 
03890   // Return the still-locked cdata.
03891   return cdataw;
03892 }
03893 
03894 ////////////////////////////////////////////////////////////////////
03895 //     Function: Texture::do_reload_ram_image
03896 //       Access: Protected, Virtual
03897 //  Description: Called when the Texture image is required but the ram
03898 //               image is not available, this will reload it from disk
03899 //               or otherwise do whatever is required to make it
03900 //               available, if possible.
03901 //
03902 //               Assumes the lock is already held.  The lock will be
03903 //               held during the duration of this operation.
03904 ////////////////////////////////////////////////////////////////////
03905 void Texture::
03906 do_reload_ram_image(CData *cdata, bool allow_compression) {
03907   BamCache *cache = BamCache::get_global_ptr();
03908   PT(BamCacheRecord) record;
03909 
03910   if (!do_has_compression(cdata)) {
03911     allow_compression = false;
03912   }
03913 
03914   if ((cache->get_cache_textures() || (allow_compression && cache->get_cache_compressed_textures())) && !textures_header_only) {
03915     // See if the texture can be found in the on-disk cache, if it is
03916     // active.
03917 
03918     record = cache->lookup(cdata->_fullpath, "txo");
03919     if (record != (BamCacheRecord *)NULL &&
03920         record->has_data()) {
03921       PT(Texture) tex = DCAST(Texture, record->get_data());
03922 
03923       // But don't use the cache record if the config parameters have
03924       // changed, and we want a different-sized texture now.
03925       int x_size = cdata->_orig_file_x_size;
03926       int y_size = cdata->_orig_file_y_size;
03927       do_adjust_this_size(cdata, x_size, y_size, cdata->_filename.get_basename(), true);
03928       if (x_size != tex->get_x_size() || y_size != tex->get_y_size()) {
03929         if (gobj_cat.is_debug()) {
03930           gobj_cat.debug()
03931             << "Cached texture " << *this << " has size "
03932             << tex->get_x_size() << " x " << tex->get_y_size()
03933             << " instead of " << x_size << " x " << y_size
03934             << "; ignoring cache.\n";
03935         }
03936       } else {
03937         // Also don't keep the cached version if it's compressed but
03938         // we want uncompressed.
03939         if (!allow_compression && tex->get_ram_image_compression() != Texture::CM_off) {
03940           if (gobj_cat.is_debug()) {
03941             gobj_cat.debug()
03942               << "Cached texture " << *this
03943               << " is compressed in cache; ignoring cache.\n";
03944           }
03945         } else {
03946           gobj_cat.info()
03947             << "Texture " << get_name() << " reloaded from disk cache\n";
03948           // We don't want to replace all the texture parameters--for
03949           // instance, we don't want to change the filter type or the
03950           // border color or anything--we just want to get the image and
03951           // necessary associated parameters.
03952           CDReader cdata_tex(tex->_cycler);
03953           cdata->_x_size = cdata_tex->_x_size;
03954           cdata->_y_size = cdata_tex->_y_size;
03955           if (cdata->_num_components != cdata_tex->_num_components) {
03956             cdata->_num_components = cdata_tex->_num_components;
03957             cdata->_format = cdata_tex->_format;
03958           }
03959           cdata->_component_type = cdata_tex->_component_type;
03960           cdata->_compression = cdata_tex->_compression;
03961           cdata->_ram_image_compression = cdata_tex->_ram_image_compression;
03962           cdata->_ram_images = cdata_tex->_ram_images;
03963           cdata->_loaded_from_image = true;
03964 
03965           bool was_compressed = (cdata->_ram_image_compression != CM_off);
03966           if (do_consider_auto_process_ram_image(cdata, uses_mipmaps(), allow_compression)) {
03967             bool is_compressed = (cdata->_ram_image_compression != CM_off);
03968             if (!was_compressed && is_compressed &&
03969                 cache->get_cache_compressed_textures()) {
03970               // We've re-compressed the image after loading it from the
03971               // cache.  To keep the cache current, rewrite it to the
03972               // cache now, in its newly compressed form.
03973               record->set_data(this, this);
03974               cache->store(record);
03975             }
03976           }
03977 
03978           return;
03979         }
03980       }
03981     }
03982   }
03983 
03984   gobj_cat.info()
03985     << "Reloading texture " << get_name() << "\n";
03986 
03987   int z = 0;
03988   int n = 0;
03989 
03990   if (cdata->_has_read_pages) {
03991     z = cdata->_z_size;
03992   }
03993   if (cdata->_has_read_mipmaps) {
03994     n = cdata->_num_mipmap_levels_read;
03995   }
03996 
03997   cdata->_loaded_from_image = false;
03998   Format orig_format = cdata->_format;
03999   int orig_num_components = cdata->_num_components;
04000 
04001   LoaderOptions options;
04002   options.set_texture_flags(LoaderOptions::TF_preload);
04003   do_read(cdata, cdata->_fullpath, cdata->_alpha_fullpath,
04004           cdata->_primary_file_num_channels, cdata->_alpha_file_channel,
04005           z, n, cdata->_has_read_pages, cdata->_has_read_mipmaps, options, NULL);
04006 
04007   if (orig_num_components == cdata->_num_components) {
04008     // Restore the original format, in case it was needlessly changed
04009     // during the reload operation.
04010     cdata->_format = orig_format;
04011   }
04012 
04013   if (do_has_ram_image(cdata) && record != (BamCacheRecord *)NULL) {
04014     if (cache->get_cache_textures() || (cdata->_ram_image_compression != CM_off && cache->get_cache_compressed_textures())) {
04015       // Update the cache.
04016       if (record != (BamCacheRecord *)NULL) {
04017         record->add_dependent_file(cdata->_fullpath);
04018       }
04019       record->set_data(this, this);
04020       cache->store(record);
04021     }
04022   }
04023 }
04024 
04025 ////////////////////////////////////////////////////////////////////
04026 //     Function: Texture::do_modify_ram_image
04027 //       Access: Protected
04028 //  Description: This is called internally to uniquify the ram image
04029 //               pointer without updating cdata->_image_modified.
04030 ////////////////////////////////////////////////////////////////////
04031 PTA_uchar Texture::
04032 do_modify_ram_image(CData *cdata) {
04033   if (cdata->_ram_images.empty() || cdata->_ram_images[0]._image.empty() ||
04034       cdata->_ram_image_compression != CM_off) {
04035     do_make_ram_image(cdata);
04036   } else {
04037     do_clear_ram_mipmap_images(cdata);
04038   }
04039   return cdata->_ram_images[0]._image;
04040 }
04041 
04042 ////////////////////////////////////////////////////////////////////
04043 //     Function: Texture::do_make_ram_image
04044 //       Access: Protected
04045 //  Description: This is called internally to make a new ram image
04046 //               without updating cdata->_image_modified.
04047 ////////////////////////////////////////////////////////////////////
04048 PTA_uchar Texture::
04049 do_make_ram_image(CData *cdata) {
04050   cdata->_ram_images.clear();
04051   cdata->_ram_images.push_back(RamImage());
04052   cdata->_ram_images[0]._page_size = do_get_expected_ram_page_size(cdata);
04053   cdata->_ram_images[0]._image = PTA_uchar::empty_array(do_get_expected_ram_image_size(cdata), get_class_type());
04054   cdata->_ram_images[0]._pointer_image = NULL;
04055   cdata->_ram_image_compression = CM_off;
04056   return cdata->_ram_images[0]._image;
04057 }
04058 
04059 ////////////////////////////////////////////////////////////////////
04060 //     Function: Texture::do_set_ram_image
04061 //       Access: Protected
04062 //  Description: Replaces the current system-RAM image with the new
04063 //               data.  If compression is not CM_off, it indicates
04064 //               that the new data is already pre-compressed in the
04065 //               indicated format.
04066 //
04067 //               This does *not* affect keep_ram_image.
04068 ////////////////////////////////////////////////////////////////////
04069 void Texture::
04070 do_set_ram_image(CData *cdata, CPTA_uchar image, Texture::CompressionMode compression,
04071                  size_t page_size) {
04072   nassertv(compression != CM_default);
04073   nassertv(compression != CM_off || image.size() == do_get_expected_ram_image_size(cdata));
04074   if (cdata->_ram_images.empty()) {
04075     cdata->_ram_images.push_back(RamImage());
04076   } else {
04077     do_clear_ram_mipmap_images(cdata);
04078   }
04079   if (page_size == 0) {
04080     page_size = image.size();
04081   }
04082   if (cdata->_ram_images[0]._image != image ||
04083       cdata->_ram_images[0]._page_size != page_size ||
04084       cdata->_ram_image_compression != compression) {
04085     cdata->_ram_images[0]._image = image.cast_non_const();
04086     cdata->_ram_images[0]._page_size = page_size;
04087     cdata->_ram_images[0]._pointer_image = NULL;
04088     cdata->_ram_image_compression = compression;
04089     ++(cdata->_image_modified);
04090   }
04091 }
04092 
04093 ////////////////////////////////////////////////////////////////////
04094 //     Function: Texture::do_modify_ram_mipmap_image
04095 //       Access: Protected
04096 //  Description: This is called internally to uniquify the nth mipmap
04097 //               image pointer without updating cdata->_image_modified.
04098 ////////////////////////////////////////////////////////////////////
04099 PTA_uchar Texture::
04100 do_modify_ram_mipmap_image(CData *cdata, int n) {
04101   nassertr(cdata->_ram_image_compression == CM_off, PTA_uchar());
04102 
04103   if (n >= (int)cdata->_ram_images.size() ||
04104       cdata->_ram_images[n]._image.empty()) {
04105     do_make_ram_mipmap_image(cdata, n);
04106   }
04107   return cdata->_ram_images[n]._image;
04108 }
04109 
04110 ////////////////////////////////////////////////////////////////////
04111 //     Function: Texture::do_make_ram_mipmap_image
04112 //       Access: Protected
04113 //  Description:
04114 ////////////////////////////////////////////////////////////////////
04115 PTA_uchar Texture::
04116 do_make_ram_mipmap_image(CData *cdata, int n) {
04117   nassertr(cdata->_ram_image_compression == CM_off, PTA_uchar(get_class_type()));
04118 
04119   while (n >= (int)cdata->_ram_images.size()) {
04120     cdata->_ram_images.push_back(RamImage());
04121   }
04122 
04123   cdata->_ram_images[n]._image = PTA_uchar::empty_array(do_get_expected_ram_mipmap_image_size(cdata, n), get_class_type());
04124   cdata->_ram_images[n]._pointer_image = NULL;
04125   cdata->_ram_images[n]._page_size = do_get_expected_ram_mipmap_page_size(cdata, n);
04126   return cdata->_ram_images[n]._image;
04127 }
04128 
04129 ////////////////////////////////////////////////////////////////////
04130 //     Function: Texture::do_set_ram_mipmap_image
04131 //       Access: Published
04132 //  Description:
04133 ////////////////////////////////////////////////////////////////////
04134 void Texture::
04135 do_set_ram_mipmap_image(CData *cdata, int n, CPTA_uchar image, size_t page_size) {
04136   nassertv(cdata->_ram_image_compression != CM_off || image.size() == do_get_expected_ram_mipmap_image_size(cdata, n));
04137 
04138   while (n >= (int)cdata->_ram_images.size()) {
04139     cdata->_ram_images.push_back(RamImage());
04140   }
04141   if (page_size == 0) {
04142     page_size = image.size();
04143   }
04144 
04145   if (cdata->_ram_images[n]._image != image ||
04146       cdata->_ram_images[n]._page_size != page_size) {
04147     cdata->_ram_images[n]._image = image.cast_non_const();
04148     cdata->_ram_images[n]._pointer_image = NULL;
04149     cdata->_ram_images[n]._page_size = page_size;
04150     ++(cdata->_image_modified);
04151   }
04152 }
04153 
04154 ////////////////////////////////////////////////////////////////////
04155 //     Function: Texture::consider_auto_process_ram_image
04156 //       Access: Protected
04157 //  Description: Should be called after a texture has been loaded into
04158 //               RAM, this considers generating mipmaps and/or
04159 //               compressing the RAM image.
04160 //
04161 //               Returns true if the image was modified by this
04162 //               operation, false if it wasn't.
04163 ////////////////////////////////////////////////////////////////////
04164 bool Texture::
04165 consider_auto_process_ram_image(bool generate_mipmaps, bool allow_compression) {
04166   CDWriter cdata(_cycler, false);
04167   return do_consider_auto_process_ram_image(cdata, generate_mipmaps, allow_compression);
04168 }
04169 
04170 ////////////////////////////////////////////////////////////////////
04171 //     Function: Texture::do_consider_auto_process_ram_image
04172 //       Access: Protected
04173 //  Description: Should be called after a texture has been loaded into
04174 //               RAM, this considers generating mipmaps and/or
04175 //               compressing the RAM image.
04176 //
04177 //               Returns true if the image was modified by this
04178 //               operation, false if it wasn't.
04179 ////////////////////////////////////////////////////////////////////
04180 bool Texture::
04181 do_consider_auto_process_ram_image(CData *cdata, bool generate_mipmaps, 
04182                                    bool allow_compression) {
04183   bool modified = false;
04184 
04185   if (generate_mipmaps && !driver_generate_mipmaps &&
04186       cdata->_ram_images.size() == 1) {
04187     do_generate_ram_mipmap_images(cdata);
04188     modified = true;
04189   }
04190 
04191   if (allow_compression && !driver_compress_textures) {
04192     CompressionMode compression = cdata->_compression;
04193     if (compression == CM_default && compressed_textures) {
04194       compression = CM_on;
04195     }
04196     if (compression != CM_off && cdata->_ram_image_compression == CM_off) {
04197       GraphicsStateGuardianBase *gsg = GraphicsStateGuardianBase::get_default_gsg();
04198       if (do_compress_ram_image(cdata, compression, QL_default, gsg)) {
04199         if (gobj_cat.is_debug()) {
04200           gobj_cat.debug()
04201             << "Compressed " << get_name() << " with "
04202             << cdata->_ram_image_compression << "\n";
04203         }
04204         modified = true;
04205       }
04206     }
04207   }
04208 
04209   return modified;
04210 }
04211 
04212 ////////////////////////////////////////////////////////////////////
04213 //     Function: Texture::do_compress_ram_image
04214 //       Access: Protected
04215 //  Description:
04216 ////////////////////////////////////////////////////////////////////
04217 bool Texture::
04218 do_compress_ram_image(CData *cdata, Texture::CompressionMode compression,
04219                       Texture::QualityLevel quality_level,
04220                       GraphicsStateGuardianBase *gsg) {
04221   nassertr(compression != CM_off, false);
04222 
04223   if (compression == CM_on) {
04224     // Select an appropriate compression mode automatically.
04225     switch (cdata->_format) {
04226     case Texture::F_rgbm:
04227     case Texture::F_rgb:
04228     case Texture::F_rgb5:
04229     case Texture::F_rgba5:
04230     case Texture::F_rgb8:
04231     case Texture::F_rgb12:
04232     case Texture::F_rgb332:
04233       if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt1)) {
04234         compression = CM_dxt1;
04235       } else if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt3)) {
04236         compression = CM_dxt3;
04237       } else if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt5)) {
04238         compression = CM_dxt5;
04239       }
04240       break;
04241 
04242     case Texture::F_rgba4:
04243       if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt3)) {
04244         compression = CM_dxt3;
04245       } else if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt5)) {
04246         compression = CM_dxt5;
04247       }
04248       break;
04249 
04250     case Texture::F_rgba:
04251     case Texture::F_rgba8:
04252     case Texture::F_rgba12:
04253     case Texture::F_rgba16:
04254     case Texture::F_rgba32:
04255       if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt5)) {
04256         compression = CM_dxt5;
04257       }
04258       break;
04259 
04260     default:
04261       break;
04262     }
04263   }
04264 
04265   // Choose an appropriate quality level.
04266   if (quality_level == Texture::QL_default) {
04267     quality_level = cdata->_quality_level;
04268   }
04269   if (quality_level == Texture::QL_default) {
04270     quality_level = texture_quality_level;
04271   }
04272 
04273 #ifdef HAVE_SQUISH
04274   if (cdata->_texture_type != TT_3d_texture &&
04275       cdata->_texture_type != TT_2d_texture_array &&
04276       cdata->_component_type == T_unsigned_byte) {
04277     int squish_flags = 0;
04278     switch (compression) {
04279     case CM_dxt1:
04280       squish_flags |= squish::kDxt1;
04281       break;
04282 
04283     case CM_dxt3:
04284       squish_flags |= squish::kDxt3;
04285       break;
04286 
04287     case CM_dxt5:
04288       squish_flags |= squish::kDxt5;
04289       break;
04290 
04291     default:
04292       break;
04293     }
04294 
04295     if (squish_flags != 0) {
04296       // This compression mode is supported by squish; use it.
04297       switch (quality_level) {
04298       case QL_fastest:
04299         squish_flags |= squish::kColourRangeFit;
04300         break;
04301 
04302       case QL_normal:
04303         // ColourClusterFit is just too slow for everyday use.
04304         squish_flags |= squish::kColourRangeFit;
04305         // squish_flags |= squish::kColourClusterFit;
04306         break;
04307 
04308       case QL_best:
04309         squish_flags |= squish::kColourIterativeClusterFit;
04310         break;
04311 
04312       default:
04313         break;
04314       }
04315 
04316       if (do_squish(cdata, compression, squish_flags)) {
04317         return true;
04318       }
04319     }
04320   }
04321 #endif  // HAVE_SQUISH
04322 
04323   return false;
04324 }
04325 
04326 ////////////////////////////////////////////////////////////////////
04327 //     Function: Texture::do_uncompress_ram_image
04328 //       Access: Protected
04329 //  Description:
04330 ////////////////////////////////////////////////////////////////////
04331 bool Texture::
04332 do_uncompress_ram_image(CData *cdata) {
04333 
04334 #ifdef HAVE_SQUISH
04335   if (cdata->_texture_type != TT_3d_texture &&
04336       cdata->_texture_type != TT_2d_texture_array &&
04337       cdata->_component_type == T_unsigned_byte) {
04338     int squish_flags = 0;
04339     switch (cdata->_ram_image_compression) {
04340     case CM_dxt1:
04341       squish_flags |= squish::kDxt1;
04342       break;
04343 
04344     case CM_dxt3:
04345       squish_flags |= squish::kDxt3;
04346       break;
04347 
04348     case CM_dxt5:
04349       squish_flags |= squish::kDxt5;
04350       break;
04351 
04352     default:
04353       break;
04354     }
04355 
04356     if (squish_flags != 0) {
04357       // This compression mode is supported by squish; use it.
04358       if (do_unsquish(cdata, squish_flags)) {
04359         return true;
04360       }
04361     }
04362   }
04363 #endif  // HAVE_SQUISH
04364   return false;
04365 }
04366 
04367 ////////////////////////////////////////////////////////////////////
04368 //     Function: Texture::do_has_all_ram_mipmap_images
04369 //       Access: Protected
04370 //  Description:
04371 ////////////////////////////////////////////////////////////////////
04372 bool Texture::
04373 do_has_all_ram_mipmap_images(const CData *cdata) const {
04374   if (cdata->_ram_images.empty() || cdata->_ram_images[0]._image.empty()) {
04375     // If we don't even have a base image, the answer is no.
04376     return false;
04377   }
04378   if (!uses_mipmaps()) {
04379     // If we have a base image and don't require mipmapping, the
04380     // answer is yes.
04381     return true;
04382   }
04383 
04384   // Check that we have enough mipmap levels to meet the size
04385   // requirements.
04386   int size = max(cdata->_x_size, max(cdata->_y_size, cdata->_z_size));
04387   int n = 0;
04388   int x = 1;
04389   while (x < size) {
04390     x = (x << 1);
04391     ++n;
04392     if (n >= (int)cdata->_ram_images.size() || cdata->_ram_images[n]._image.empty()) {
04393       return false;
04394     }
04395   }
04396 
04397   return true;
04398 }
04399 
04400 ////////////////////////////////////////////////////////////////////
04401 //     Function: Texture::do_reconsider_z_size
04402 //       Access: Protected
04403 //  Description: Considers whether the z_size (or num_views) should
04404 //               automatically be adjusted when the user loads a new
04405 //               page.  Returns true if the z size is valid, false
04406 //               otherwise.
04407 //
04408 //               Assumes the lock is already held.
04409 ////////////////////////////////////////////////////////////////////
04410 bool Texture::
04411 do_reconsider_z_size(CData *cdata, int z, const LoaderOptions &options) {
04412   if (z >= cdata->_z_size * cdata->_num_views) {
04413     bool num_views_specified = true;
04414     if (options.get_texture_flags() & LoaderOptions::TF_multiview) {
04415       // This flag is false if is a multiview texture with a specified
04416       // number of views.  It is true if it is not a multiview
04417       // texture, or if it is but the number of views is explicitly
04418       // specified.
04419       num_views_specified = (options.get_texture_num_views() != 0);
04420     }
04421 
04422     if (num_views_specified &&
04423         (cdata->_texture_type == Texture::TT_3d_texture ||
04424          cdata->_texture_type == Texture::TT_2d_texture_array)) {
04425       // If we're loading a page past _z_size, treat it as an implicit
04426       // request to enlarge _z_size.  However, this is only legal if
04427       // this is, in fact, a 3-d texture or a 2d texture array (cube maps
04428       // always have z_size 6, and other types have z_size 1).
04429       nassertr(cdata->_num_views != 0, false);
04430       cdata->_z_size = (z / cdata->_num_views) + 1;
04431 
04432     } else if (cdata->_z_size != 0) {
04433       // In the case of a 2-d texture or cube map, or a 3-d texture
04434       // with an unspecified _num_views, assume we're loading views of
04435       // a multiview texture.
04436       cdata->_num_views = (z / cdata->_z_size) + 1;
04437 
04438     } else {
04439       // The first image loaded sets an implicit z-size.
04440       cdata->_z_size = 1;
04441     }
04442 
04443     // Increase the size of the data buffer to make room for the new
04444     // texture level.
04445     do_allocate_pages(cdata);
04446   }
04447 
04448   return true;
04449 }
04450 
04451 ////////////////////////////////////////////////////////////////////
04452 //     Function: Texture::do_allocate_pages
04453 //       Access: Protected, Virtual
04454 //  Description: Called internally by do_reconsider_z_size() to
04455 //               allocate new memory in _ram_images[0] for the new
04456 //               number of pages.
04457 //
04458 //               Assumes the lock is already held.
04459 ////////////////////////////////////////////////////////////////////
04460 void Texture::
04461 do_allocate_pages(CData *cdata) {
04462   size_t new_size = do_get_expected_ram_image_size(cdata);
04463   if (!cdata->_ram_images.empty() &&
04464       !cdata->_ram_images[0]._image.empty() &&
04465       new_size > cdata->_ram_images[0]._image.size()) {
04466     cdata->_ram_images[0]._image.insert(cdata->_ram_images[0]._image.end(), new_size - cdata->_ram_images[0]._image.size(), 0);
04467     nassertv(cdata->_ram_images[0]._image.size() == new_size);
04468   }
04469 }
04470 
04471 ////////////////////////////////////////////////////////////////////
04472 //     Function: Texture::do_reconsider_image_properties
04473 //       Access: Protected
04474 //  Description: Resets the internal Texture properties when a new
04475 //               image file is loaded.  Returns true if the new image
04476 //               is valid, false otherwise.
04477 //
04478 //               Assumes the lock is already held.
04479 ////////////////////////////////////////////////////////////////////
04480 bool Texture::
04481 do_reconsider_image_properties(CData *cdata, int x_size, int y_size, int num_components,
04482                                Texture::ComponentType component_type, int z,
04483                                const LoaderOptions &options) {
04484   if (!cdata->_loaded_from_image || num_components != cdata->_num_components) {
04485     // Come up with a default format based on the number of channels.
04486     // But only do this the first time the file is loaded, or if the
04487     // number of channels in the image changes on subsequent loads.
04488 
04489     switch (num_components) {
04490     case 1:
04491       cdata->_format = F_luminance;
04492       break;
04493 
04494     case 2:
04495       cdata->_format = F_luminance_alpha;
04496       break;
04497 
04498     case 3:
04499       cdata->_format = F_rgb;
04500       break;
04501 
04502     case 4:
04503       cdata->_format = F_rgba;
04504       break;
04505 
04506     default:
04507       // Eh?
04508       nassertr(false, false);
04509       cdata->_format = F_rgb;
04510     }
04511   }
04512 
04513   if (!cdata->_loaded_from_image) {
04514     if ((options.get_texture_flags() & LoaderOptions::TF_allow_1d) &&
04515         cdata->_texture_type == TT_2d_texture && x_size != 1 && y_size == 1) {
04516       // If we're loading an Nx1 size texture, infer a 1-d texture type.
04517       cdata->_texture_type = TT_1d_texture;
04518     }
04519 
04520 #ifndef NDEBUG
04521     if (cdata->_texture_type == TT_1d_texture) {
04522       nassertr(y_size == 1, false);
04523     } else if (cdata->_texture_type == TT_cube_map) {
04524       nassertr(x_size == y_size, false);
04525     }
04526 #endif
04527     if ((cdata->_x_size != x_size)||(cdata->_y_size != y_size)) {
04528       do_set_pad_size(cdata, 0, 0, 0);
04529     }
04530     cdata->_x_size = x_size;
04531     cdata->_y_size = y_size;
04532     cdata->_num_components = num_components;
04533     do_set_component_type(cdata, component_type);
04534 
04535   } else {
04536     if (cdata->_x_size != x_size ||
04537         cdata->_y_size != y_size ||
04538         cdata->_num_components != num_components ||
04539         cdata->_component_type != component_type) {
04540       gobj_cat.error()
04541         << "Texture properties have changed for texture " << get_name()
04542         << " page " << z << ".\n";
04543       return false;
04544     }
04545   }
04546 
04547   return true;
04548 }
04549 
04550 ////////////////////////////////////////////////////////////////////
04551 //     Function: Texture::do_rescale_texture
04552 //       Access: Private
04553 //  Description:
04554 ////////////////////////////////////////////////////////////////////
04555 bool Texture::
04556 do_rescale_texture(CData *cdata) {
04557   int new_x_size = cdata->_x_size;
04558   int new_y_size = cdata->_y_size;
04559   if (cdata->_z_size * cdata->_num_views != 1) {
04560     nassert_raise("rescale_texture() doesn't support 3-d or multiview textures.");
04561     return false;
04562   }
04563 
04564   if (do_adjust_this_size(cdata, new_x_size, new_y_size, get_name(), false)) {
04565     // OK, we have to scale the image.
04566     PNMImage orig_image;
04567     if (!do_store_one(cdata, orig_image, 0, 0)) {
04568       gobj_cat.warning()
04569         << "Couldn't get image in rescale_texture()\n";
04570       return false;
04571     }
04572 
04573     gobj_cat.info()
04574       << "Resizing " << get_name() << " to " << new_x_size << " x "
04575       << new_y_size << "\n";
04576     PNMImage new_image(new_x_size, new_y_size, orig_image.get_num_channels(),
04577                        orig_image.get_maxval());
04578     new_image.quick_filter_from(orig_image);
04579 
04580     do_clear_ram_image(cdata);
04581     ++(cdata->_image_modified);
04582     if (!do_load_one(cdata, new_image, get_name(), 0, 0, LoaderOptions())) {
04583       return false;
04584     }
04585 
04586     return true;
04587   }
04588 
04589   // Maybe we should pad the image.
04590   int pad_x_size = 0;
04591   int pad_y_size = 0;
04592   if (do_get_auto_texture_scale(cdata) == ATS_pad) {
04593     new_x_size = cdata->_x_size;
04594     new_y_size = cdata->_y_size;
04595     if (do_adjust_this_size(cdata, new_x_size, new_y_size, get_name(), true)) {
04596       pad_x_size = new_x_size - cdata->_x_size;
04597       pad_y_size = new_y_size - cdata->_y_size;
04598 
04599       PNMImage orig_image;
04600       if (!do_store_one(cdata, orig_image, 0, 0)) {
04601         gobj_cat.warning()
04602           << "Couldn't get image in rescale_texture()\n";
04603         return false;
04604       }
04605       PNMImage new_image(new_x_size, new_y_size, orig_image.get_num_channels(),
04606                          orig_image.get_maxval());
04607       new_image.copy_sub_image(orig_image, 0, new_y_size - orig_image.get_y_size());
04608 
04609       do_clear_ram_image(cdata);
04610       cdata->_loaded_from_image = false;
04611       ++(cdata->_image_modified);
04612       if (!do_load_one(cdata, new_image, get_name(), 0, 0, LoaderOptions())) {
04613         return false;
04614       }
04615 
04616       do_set_pad_size(cdata, pad_x_size, pad_y_size, 0);
04617       return true;
04618     }
04619   }
04620 
04621   // No changes needed.
04622   return false;
04623 }
04624 
04625 ////////////////////////////////////////////////////////////////////
04626 //     Function: Texture::make_copy_impl
04627 //       Access: Protected, Virtual
04628 //  Description:
04629 ////////////////////////////////////////////////////////////////////
04630 PT(Texture) Texture::
04631 make_copy_impl() const {
04632   CDReader cdata(_cycler);
04633   return do_make_copy(cdata);
04634 }
04635 
04636 ////////////////////////////////////////////////////////////////////
04637 //     Function: Texture::do_make_copy
04638 //       Access: Protected
04639 //  Description:
04640 ////////////////////////////////////////////////////////////////////
04641 PT(Texture) Texture::
04642 do_make_copy(const CData *cdata) const {
04643   PT(Texture) tex = new Texture(get_name());
04644   CDWriter cdata_tex(tex->_cycler, true);
04645   tex->do_assign(cdata_tex, this, cdata);
04646   return tex;
04647 }
04648 
04649 ////////////////////////////////////////////////////////////////////
04650 //     Function: Texture::do_assign
04651 //       Access: Protected
04652 //  Description: The internal implementation of operator =().  Assumes
04653 //               the lock is already held on both Textures.
04654 ////////////////////////////////////////////////////////////////////
04655 void Texture::
04656 do_assign(CData *cdata, const Texture *copy, const CData *cdata_copy) {
04657   cdata->do_assign(cdata_copy);
04658 }
04659 
04660 ////////////////////////////////////////////////////////////////////
04661 //     Function: Texture::do_clear
04662 //       Access: Protected, Virtual
04663 //  Description: The protected implementation of clear().  Assumes the
04664 //               lock is already held.
04665 ////////////////////////////////////////////////////////////////////
04666 void Texture::
04667 do_clear(CData *cdata) {
04668   Texture tex;
04669   tex.local_object();
04670   CDReader cdata_tex(tex._cycler);
04671   do_assign(cdata, &tex, cdata_tex);
04672 
04673   ++(cdata->_properties_modified);
04674   ++(cdata->_image_modified);
04675   ++(cdata->_simple_image_modified);
04676 }
04677 
04678 ////////////////////////////////////////////////////////////////////
04679 //     Function: Texture::do_setup_texture
04680 //       Access: Protected
04681 //  Description:
04682 ////////////////////////////////////////////////////////////////////
04683 void Texture::
04684 do_setup_texture(CData *cdata, Texture::TextureType texture_type, int x_size, int y_size,
04685                  int z_size, Texture::ComponentType component_type,
04686                  Texture::Format format) {
04687   switch (texture_type) {
04688   case TT_1d_texture:
04689     nassertv(y_size == 1 && z_size == 1);
04690     break;
04691 
04692   case TT_2d_texture:
04693     nassertv(z_size == 1);
04694     break;
04695 
04696   case TT_3d_texture:
04697     break;
04698 
04699   case TT_2d_texture_array:
04700     break;
04701 
04702   case TT_cube_map:
04703     // Cube maps must always consist of six square images.
04704     nassertv(x_size == y_size && z_size == 6);
04705 
04706     // In principle the wrap mode shouldn't mean anything to a cube
04707     // map, but some drivers seem to misbehave if it's other than
04708     // WM_clamp.
04709     cdata->_wrap_u = WM_clamp;
04710     cdata->_wrap_v = WM_clamp;
04711     cdata->_wrap_w = WM_clamp;
04712     break;
04713   }
04714 
04715   if (texture_type != TT_2d_texture) {
04716     do_clear_simple_ram_image(cdata);
04717   }
04718 
04719   cdata->_texture_type = texture_type;
04720   cdata->_x_size = x_size;
04721   cdata->_y_size = y_size;
04722   cdata->_z_size = z_size;
04723   cdata->_num_views = 1;
04724   do_set_component_type(cdata, component_type);
04725   do_set_format(cdata, format);
04726 
04727   do_clear_ram_image(cdata);
04728   do_set_pad_size(cdata, 0, 0, 0);
04729   cdata->_orig_file_x_size = 0;
04730   cdata->_orig_file_y_size = 0;
04731   cdata->_loaded_from_image = false;
04732   cdata->_loaded_from_txo = false;
04733   cdata->_has_read_pages = false;
04734   cdata->_has_read_mipmaps = false;
04735 }
04736 
04737 ////////////////////////////////////////////////////////////////////
04738 //     Function: Texture::do_set_format
04739 //       Access: Protected
04740 //  Description:
04741 ////////////////////////////////////////////////////////////////////
04742 void Texture::
04743 do_set_format(CData *cdata, Texture::Format format) {
04744   if (format == cdata->_format) {
04745     return;
04746   }
04747   cdata->_format = format;
04748   ++(cdata->_properties_modified);
04749 
04750   switch (cdata->_format) {
04751   case F_color_index:
04752   case F_depth_stencil:
04753   case F_depth_component:
04754   case F_depth_component16:
04755   case F_depth_component24:
04756   case F_depth_component32:
04757   case F_red:
04758   case F_green:
04759   case F_blue:
04760   case F_alpha:
04761   case F_luminance:
04762     cdata->_num_components = 1;
04763     break;
04764 
04765   case F_luminance_alpha:
04766   case F_luminance_alphamask:
04767     cdata->_num_components = 2;
04768     break;
04769 
04770   case F_rgb:
04771   case F_rgb5:
04772   case F_rgb8:
04773   case F_rgb12:
04774   case F_rgb332:
04775     cdata->_num_components = 3;
04776     break;
04777 
04778   case F_rgba:
04779   case F_rgbm:
04780   case F_rgba4:
04781   case F_rgba5:
04782   case F_rgba8:
04783   case F_rgba12:
04784   case F_rgba16:
04785   case F_rgba32:
04786     cdata->_num_components = 4;
04787     break;
04788   }
04789 }
04790 
04791 ////////////////////////////////////////////////////////////////////
04792 //     Function: Texture::do_set_component_type
04793 //       Access: Protected
04794 //  Description:
04795 ////////////////////////////////////////////////////////////////////
04796 void Texture::
04797 do_set_component_type(CData *cdata, Texture::ComponentType component_type) {
04798   cdata->_component_type = component_type;
04799 
04800   switch (component_type) {
04801   case T_unsigned_byte:
04802     cdata->_component_width = 1;
04803     break;
04804 
04805   case T_unsigned_short:
04806     cdata->_component_width = 2;
04807     break;
04808 
04809   case T_float:
04810     cdata->_component_width = 4;
04811     break;
04812 
04813   case T_unsigned_int_24_8:
04814     //FIXME: I have no idea...
04815     break;
04816   }
04817 }
04818 
04819 ////////////////////////////////////////////////////////////////////
04820 //     Function: Texture::do_set_x_size
04821 //       Access: Protected
04822 //  Description:
04823 ////////////////////////////////////////////////////////////////////
04824 void Texture::
04825 do_set_x_size(CData *cdata, int x_size) {
04826   if (cdata->_x_size != x_size) {
04827     cdata->_x_size = x_size;
04828     ++(cdata->_image_modified);
04829     do_clear_ram_image(cdata);
04830     do_set_pad_size(cdata, 0, 0, 0);
04831   }
04832 }
04833 
04834 ////////////////////////////////////////////////////////////////////
04835 //     Function: Texture::do_set_y_size
04836 //       Access: Protected
04837 //  Description:
04838 ////////////////////////////////////////////////////////////////////
04839 void Texture::
04840 do_set_y_size(CData *cdata, int y_size) {
04841   if (cdata->_y_size != y_size) {
04842     nassertv(cdata->_texture_type != Texture::TT_1d_texture || y_size == 1);
04843     cdata->_y_size = y_size;
04844     ++(cdata->_image_modified);
04845     do_clear_ram_image(cdata);
04846     do_set_pad_size(cdata, 0, 0, 0);
04847   }
04848 }
04849 
04850 ////////////////////////////////////////////////////////////////////
04851 //     Function: Texture::do_set_z_size
04852 //       Access: Protected
04853 //  Description: Changes the z size indicated for the texture.  This
04854 //               also implicitly unloads the texture if it has already
04855 //               been loaded.
04856 ////////////////////////////////////////////////////////////////////
04857 void Texture::
04858 do_set_z_size(CData *cdata, int z_size) {
04859   if (cdata->_z_size != z_size) {
04860     nassertv((cdata->_texture_type == Texture::TT_3d_texture) ||
04861              (cdata->_texture_type == Texture::TT_cube_map && z_size == 6) ||
04862              (cdata->_texture_type == Texture::TT_2d_texture_array) || (z_size == 1));
04863     cdata->_z_size = z_size;
04864     ++(cdata->_image_modified);
04865     do_clear_ram_image(cdata);
04866     do_set_pad_size(cdata, 0, 0, 0);
04867   }
04868 }
04869 
04870 ////////////////////////////////////////////////////////////////////
04871 //     Function: Texture::do_set_num_views
04872 //       Access: Protected
04873 //  Description:
04874 ////////////////////////////////////////////////////////////////////
04875 void Texture::
04876 do_set_num_views(CData *cdata, int num_views) {
04877   nassertv(num_views >= 1);
04878   if (cdata->_num_views != num_views) {
04879     cdata->_num_views = num_views;
04880     ++(cdata->_image_modified);
04881     do_clear_ram_image(cdata);
04882     do_set_pad_size(cdata, 0, 0, 0);
04883   }
04884 }
04885 
04886 ////////////////////////////////////////////////////////////////////
04887 //     Function: Texture::do_set_wrap_u
04888 //       Access: Protected
04889 //  Description:
04890 ////////////////////////////////////////////////////////////////////
04891 void Texture::
04892 do_set_wrap_u(CData *cdata, Texture::WrapMode wrap) {
04893   if (cdata->_wrap_u != wrap) {
04894     ++(cdata->_properties_modified);
04895     cdata->_wrap_u = wrap;
04896   }
04897 }
04898 
04899 ////////////////////////////////////////////////////////////////////
04900 //     Function: Texture::do_set_wrap_v
04901 //       Access: Protected
04902 //  Description:
04903 ////////////////////////////////////////////////////////////////////
04904 void Texture::
04905 do_set_wrap_v(CData *cdata, Texture::WrapMode wrap) {
04906   if (cdata->_wrap_v != wrap) {
04907     ++(cdata->_properties_modified);
04908     cdata->_wrap_v = wrap;
04909   }
04910 }
04911 
04912 ////////////////////////////////////////////////////////////////////
04913 //     Function: Texture::do_set_wrap_w
04914 //       Access: Protected
04915 //  Description:
04916 ////////////////////////////////////////////////////////////////////
04917 void Texture::
04918 do_set_wrap_w(CData *cdata, Texture::WrapMode wrap) {
04919   if (cdata->_wrap_w != wrap) {
04920     ++(cdata->_properties_modified);
04921     cdata->_wrap_w = wrap;
04922   }
04923 }
04924 
04925 ////////////////////////////////////////////////////////////////////
04926 //     Function: Texture::do_set_minfilter
04927 //       Access: Protected
04928 //  Description:
04929 ////////////////////////////////////////////////////////////////////
04930 void Texture::
04931 do_set_minfilter(CData *cdata, Texture::FilterType filter) {
04932   if (cdata->_minfilter != filter) {
04933     ++(cdata->_properties_modified);
04934     cdata->_minfilter = filter;
04935   }
04936 }
04937 
04938 ////////////////////////////////////////////////////////////////////
04939 //     Function: Texture::do_set_magfilter
04940 //       Access: Protected
04941 //  Description:
04942 ////////////////////////////////////////////////////////////////////
04943 void Texture::
04944 do_set_magfilter(CData *cdata, Texture::FilterType filter) {
04945   if (cdata->_magfilter != filter) {
04946     ++(cdata->_properties_modified);
04947     cdata->_magfilter = filter;
04948   }
04949 }
04950 
04951 ////////////////////////////////////////////////////////////////////
04952 //     Function: Texture::do_set_anisotropic_degree
04953 //       Access: Protected
04954 //  Description:
04955 ////////////////////////////////////////////////////////////////////
04956 void Texture::
04957 do_set_anisotropic_degree(CData *cdata, int anisotropic_degree) {
04958   if (cdata->_anisotropic_degree != anisotropic_degree) {
04959     ++(cdata->_properties_modified);
04960     cdata->_anisotropic_degree = anisotropic_degree;
04961   }
04962 }
04963 
04964 ////////////////////////////////////////////////////////////////////
04965 //     Function: Texture::do_set_border_color
04966 //       Access: Protected
04967 //  Description:
04968 ////////////////////////////////////////////////////////////////////
04969 void Texture::
04970 do_set_border_color(CData *cdata, const LColor &color) {
04971   if (cdata->_border_color != color) {
04972     ++(cdata->_properties_modified);
04973     cdata->_border_color = color;
04974   }
04975 }
04976 
04977 ////////////////////////////////////////////////////////////////////
04978 //     Function: Texture::do_set_compression
04979 //       Access: Protected
04980 //  Description:
04981 ////////////////////////////////////////////////////////////////////
04982 void Texture::
04983 do_set_compression(CData *cdata, Texture::CompressionMode compression) {
04984   if (cdata->_compression != compression) {
04985     ++(cdata->_properties_modified);
04986     cdata->_compression = compression;
04987 
04988     if (do_has_ram_image(cdata)) {
04989       bool has_compression = do_has_compression(cdata);
04990       bool has_ram_image_compression = (cdata->_ram_image_compression != CM_off);
04991       if (has_compression != has_ram_image_compression ||
04992           has_compression) {
04993         // Reload if we're turning compression on or off, or if we're
04994         // changing the compression mode to a different kind of
04995         // compression.
04996         do_reload(cdata);
04997       }
04998     }
04999   }
05000 }
05001 
05002 ////////////////////////////////////////////////////////////////////
05003 //     Function: Texture::do_set_quality_level
05004 //       Access: Public
05005 //  Description:
05006 ////////////////////////////////////////////////////////////////////
05007 void Texture::
05008 do_set_quality_level(CData *cdata, Texture::QualityLevel quality_level) {
05009   if (cdata->_quality_level != quality_level) {
05010     ++(cdata->_properties_modified);
05011     cdata->_quality_level = quality_level;
05012   }
05013 }
05014 
05015 ////////////////////////////////////////////////////////////////////
05016 //     Function: Texture::do_has_compression
05017 //       Access: Protected
05018 //  Description:
05019 ////////////////////////////////////////////////////////////////////
05020 bool Texture::
05021 do_has_compression(const CData *cdata) const {
05022   if (cdata->_compression == CM_default) {
05023     return compressed_textures;
05024   } else {
05025     return (cdata->_compression != CM_off);
05026   }
05027 }
05028 
05029 ////////////////////////////////////////////////////////////////////
05030 //     Function: Texture::do_has_ram_image
05031 //       Access: Protected, Virtual
05032 //  Description: The protected implementation of has_ram_image().
05033 //               Assumes the lock is already held.
05034 ////////////////////////////////////////////////////////////////////
05035 bool Texture::
05036 do_has_ram_image(const CData *cdata) const {
05037   return !cdata->_ram_images.empty() && !cdata->_ram_images[0]._image.empty();
05038 }
05039 
05040 ////////////////////////////////////////////////////////////////////
05041 //     Function: Texture::do_has_uncompressed_ram_image
05042 //       Access: Protected, Virtual
05043 //  Description: The protected implementation of
05044 //               has_uncompressed_ram_image().  Assumes the lock is
05045 //               already held.
05046 ////////////////////////////////////////////////////////////////////
05047 bool Texture::
05048 do_has_uncompressed_ram_image(const CData *cdata) const {
05049   return !cdata->_ram_images.empty() && !cdata->_ram_images[0]._image.empty() && cdata->_ram_image_compression == CM_off;
05050 }
05051 
05052 ////////////////////////////////////////////////////////////////////
05053 //     Function: Texture::do_get_ram_image
05054 //       Access: Protected
05055 //  Description:
05056 ////////////////////////////////////////////////////////////////////
05057 CPTA_uchar Texture::
05058 do_get_ram_image(CData *cdata) {
05059   if (!do_has_ram_image(cdata) && do_can_reload(cdata)) {
05060     do_reload_ram_image(cdata, true);
05061 
05062     // Normally, we don't update the cdata->_modified semaphores in a do_blah
05063     // method, but we'll make an exception in this case, because it's
05064     // easiest to modify these here, and only when we know it's
05065     // needed.
05066     ++(cdata->_image_modified);
05067     ++(cdata->_properties_modified);
05068   }
05069 
05070   if (cdata->_ram_images.empty()) {
05071     return CPTA_uchar(get_class_type());
05072   }
05073 
05074   return cdata->_ram_images[0]._image;
05075 }
05076 
05077 ////////////////////////////////////////////////////////////////////
05078 //     Function: Texture::do_get_uncompressed_ram_image
05079 //       Access: Protected
05080 //  Description:
05081 ////////////////////////////////////////////////////////////////////
05082 CPTA_uchar Texture::
05083 do_get_uncompressed_ram_image(CData *cdata) {
05084   if (!cdata->_ram_images.empty() && cdata->_ram_image_compression != CM_off) {
05085     // We have an image in-ram, but it's compressed.  Try to
05086     // uncompress it first.
05087     if (do_uncompress_ram_image(cdata)) {
05088       if (gobj_cat.is_debug()) {
05089         gobj_cat.debug()
05090           << "Uncompressed " << get_name() << "\n";
05091       }
05092       return cdata->_ram_images[0]._image;
05093     }
05094   }
05095 
05096   // Couldn't uncompress the existing image.  Try to reload it.
05097   if ((!do_has_ram_image(cdata) || cdata->_ram_image_compression != CM_off) && do_can_reload(cdata)) {
05098     do_reload_ram_image(cdata, false);
05099   }
05100 
05101   if (!cdata->_ram_images.empty() && cdata->_ram_image_compression != CM_off) {
05102     // Great, now we have an image.
05103     if (do_uncompress_ram_image(cdata)) {
05104       gobj_cat.info()
05105         << "Uncompressed " << get_name() << "\n";
05106       return cdata->_ram_images[0]._image;
05107     }
05108   }
05109 
05110   if (cdata->_ram_images.empty() || cdata->_ram_image_compression != CM_off) {
05111     return CPTA_uchar(get_class_type());
05112   }
05113 
05114   return cdata->_ram_images[0]._image;
05115 }
05116 
05117 ////////////////////////////////////////////////////////////////////
05118 //     Function: Texture::get_ram_image_as
05119 //       Access: Published
05120 //  Description: Returns the uncompressed system-RAM image data
05121 //               associated with the texture. Rather than
05122 //               just returning a pointer to the data, like
05123 //               get_uncompressed_ram_image, this function first
05124 //               processes the data and reorders the components
05125 //               using the specified format string, and places these
05126 //               into a new char array. The 'format' argument should
05127 //               specify in which order the components of the texture
05128 //               must be. For example, valid format strings are
05129 //               "RGBA", "GA", "ABRG" or "AAA". A component can
05130 //               also be written as "0" or "1", which means an
05131 //               empty/black or a full/white channel, respectively.
05132 //               This function is particularly useful to
05133 //               copy an image in-memory to a different library
05134 //               (for example, PIL or wxWidgets) that require
05135 //               a different component order than Panda's internal
05136 //               format, BGRA. Note, however, that this conversion
05137 //               can still be too slow if you want to do it every
05138 //               frame, and should thus be avoided for that purpose.
05139 //               The only requirement for the reordering is that
05140 //               an uncompressed image must be available. If the
05141 //               RAM image is compressed, it will attempt to re-load
05142 //               the texture from disk, if it doesn't find an
05143 //               uncompressed image there, it will return NULL.
05144 ////////////////////////////////////////////////////////////////////
05145 CPTA_uchar Texture::
05146 get_ram_image_as(const string &requested_format) {
05147   CDWriter cdata(_cycler, false);
05148   string format = upcase(requested_format);
05149 
05150   // Make sure we can grab something that's uncompressed.
05151   CPTA_uchar data = do_get_uncompressed_ram_image(cdata);
05152   if (data == NULL) {
05153     gobj_cat.error() << "Couldn't find an uncompressed RAM image!\n";
05154     return CPTA_uchar(get_class_type());
05155   }
05156   int imgsize = cdata->_x_size * cdata->_y_size;
05157   nassertr(cdata->_num_components > 0 && cdata->_num_components <= 4, CPTA_uchar(get_class_type()));
05158   nassertr(data.size() == (size_t)(cdata->_component_width * cdata->_num_components * imgsize), CPTA_uchar(get_class_type()));
05159 
05160   // Check if the format is already what we have internally.
05161   if ((cdata->_num_components == 1 && format.size() == 1) ||
05162       (cdata->_num_components == 2 && format.size() == 2 && format.at(1) == 'A' && format.at(0) != 'A') ||
05163       (cdata->_num_components == 3 && format == "BGR") ||
05164       (cdata->_num_components == 4 && format == "BGRA")) {
05165     // The format string is already our format, so we just need to copy it.
05166     return CPTA_uchar(data);
05167   }
05168 
05169   // Create a new empty array that can hold our image.
05170   PTA_uchar newdata = PTA_uchar::empty_array(imgsize * format.size() * cdata->_component_width, get_class_type());
05171 
05172   // These ifs are for optimization of commonly used image types.
05173   if (format == "RGBA" && cdata->_num_components == 4 && cdata->_component_width == 1) {
05174     imgsize *= 4;
05175     for (int p = 0; p < imgsize; p += 4) {
05176       newdata[p    ] = data[p + 2];
05177       newdata[p + 1] = data[p + 1];
05178       newdata[p + 2] = data[p    ];
05179       newdata[p + 3] = data[p + 3];
05180     }
05181     return newdata;
05182   }
05183   if (format == "RGB" && cdata->_num_components == 3 && cdata->_component_width == 1) {
05184     imgsize *= 3;
05185     for (int p = 0; p < imgsize; p += 3) {
05186       newdata[p    ] = data[p + 2];
05187       newdata[p + 1] = data[p + 1];
05188       newdata[p + 2] = data[p    ];
05189     }
05190     return newdata;
05191   }
05192   if (format == "A" && cdata->_component_width == 1 && cdata->_num_components != 3) {
05193     // We can generally rely on alpha to be the last component.
05194     int component = cdata->_num_components - 1;
05195     for (int p = 0; p < imgsize; ++p) {
05196       newdata[p] = data[component];
05197     }
05198     return newdata;
05199   }
05200   if (cdata->_component_width == 1) {
05201     for (int p = 0; p < imgsize; ++p) {
05202       for (uchar s = 0; s < format.size(); ++s) {
05203         signed char component = -1;
05204         if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
05205           component = 0;
05206         } else if (format.at(s) == 'G') {
05207           component = 1;
05208         } else if (format.at(s) == 'R') {
05209           component = 2;
05210         } else if (format.at(s) == 'A') {
05211           nassertr(cdata->_num_components != 3, CPTA_uchar(get_class_type()));
05212           component = cdata->_num_components - 1;
05213         } else if (format.at(s) == '0') {
05214           newdata[p * format.size() + s] = 0x00;
05215         } else if (format.at(s) == '1') {
05216           newdata[p * format.size() + s] = 0xff;
05217         } else {
05218           gobj_cat.error() << "Unexpected component character '"
05219             << format.at(s) << "', expected one of RGBA!\n";
05220           return CPTA_uchar(get_class_type());
05221         }
05222         if (component >= 0) {
05223           newdata[p * format.size() + s] = data[p * cdata->_num_components + component];
05224         }
05225       }
05226     }
05227     return newdata;
05228   }
05229   for (int p = 0; p < imgsize; ++p) {
05230     for (uchar s = 0; s < format.size(); ++s) {
05231       signed char component = -1;
05232       if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
05233         component = 0;
05234       } else if (format.at(s) == 'G') {
05235         component = 1;
05236       } else if (format.at(s) == 'R') {
05237         component = 2;
05238       } else if (format.at(s) == 'A') {
05239         nassertr(cdata->_num_components != 3, CPTA_uchar(get_class_type()));
05240         component = cdata->_num_components - 1;
05241       } else if (format.at(s) == '0') {
05242         memset((void*)(newdata + (p * format.size() + s) * cdata->_component_width),  0, cdata->_component_width);
05243       } else if (format.at(s) == '1') {
05244         memset((void*)(newdata + (p * format.size() + s) * cdata->_component_width), -1, cdata->_component_width);
05245       } else {
05246         gobj_cat.error() << "Unexpected component character '"
05247           << format.at(s) << "', expected one of RGBA!\n";
05248         return CPTA_uchar(get_class_type());
05249       }
05250       if (component >= 0) {
05251         memcpy((void*)(newdata + (p * format.size() + s) * cdata->_component_width),
05252                (void*)(data + (p * cdata->_num_components + component) * cdata->_component_width),
05253                cdata->_component_width);
05254       }
05255     }
05256   }
05257   return newdata;
05258 }
05259 
05260 ////////////////////////////////////////////////////////////////////
05261 //     Function: Texture::do_set_simple_ram_image
05262 //       Access: Protected
05263 //  Description:
05264 ////////////////////////////////////////////////////////////////////
05265 void Texture::
05266 do_set_simple_ram_image(CData *cdata, CPTA_uchar image, int x_size, int y_size) {
05267   nassertv(cdata->_texture_type == TT_2d_texture);
05268   size_t expected_page_size = (size_t)(x_size * y_size * 4);
05269   nassertv(image.size() == expected_page_size);
05270 
05271   cdata->_simple_x_size = x_size;
05272   cdata->_simple_y_size = y_size;
05273   cdata->_simple_ram_image._image = image.cast_non_const();
05274   cdata->_simple_ram_image._page_size = image.size();
05275   cdata->_simple_image_date_generated = (PN_int32)time(NULL);
05276   ++(cdata->_simple_image_modified);
05277 }
05278 
05279 ////////////////////////////////////////////////////////////////////
05280 //     Function: Texture::do_get_expected_num_mipmap_levels
05281 //       Access: Protected
05282 //  Description:
05283 ////////////////////////////////////////////////////////////////////
05284 int Texture::
05285 do_get_expected_num_mipmap_levels(const CData *cdata) const {
05286   int size = max(cdata->_x_size, max(cdata->_y_size, cdata->_z_size));
05287   int count = 1;
05288   while (size > 1) {
05289     size >>= 1;
05290     ++count;
05291   }
05292   return count;
05293 }
05294 
05295 ////////////////////////////////////////////////////////////////////
05296 //     Function: Texture::do_get_ram_mipmap_page_size
05297 //       Access: Protected
05298 //  Description:
05299 ////////////////////////////////////////////////////////////////////
05300 size_t Texture::
05301 do_get_ram_mipmap_page_size(const CData *cdata, int n) const {
05302   if (cdata->_ram_image_compression != CM_off) {
05303     if (n >= 0 && n < (int)cdata->_ram_images.size()) {
05304       return cdata->_ram_images[n]._page_size;
05305     }
05306     return 0;
05307   } else {
05308     return do_get_expected_ram_mipmap_page_size(cdata, n);
05309   }
05310 }
05311 
05312 ////////////////////////////////////////////////////////////////////
05313 //     Function: Texture::do_get_expected_mipmap_x_size
05314 //       Access: Protected
05315 //  Description:
05316 ////////////////////////////////////////////////////////////////////
05317 int Texture::
05318 do_get_expected_mipmap_x_size(const CData *cdata, int n) const {
05319   int size = max(cdata->_x_size, 1);
05320   while (n > 0 && size > 1) {
05321     size >>= 1;
05322     --n;
05323   }
05324   return size;
05325 }
05326 
05327 ////////////////////////////////////////////////////////////////////
05328 //     Function: Texture::do_get_expected_mipmap_y_size
05329 //       Access: Protected
05330 //  Description:
05331 ////////////////////////////////////////////////////////////////////
05332 int Texture::
05333 do_get_expected_mipmap_y_size(const CData *cdata, int n) const {
05334   int size = max(cdata->_y_size, 1);
05335   while (n > 0 && size > 1) {
05336     size >>= 1;
05337     --n;
05338   }
05339   return size;
05340 }
05341 
05342 ////////////////////////////////////////////////////////////////////
05343 //     Function: Texture::do_get_expected_mipmap_z_size
05344 //       Access: Protected
05345 //  Description:
05346 ////////////////////////////////////////////////////////////////////
05347 int Texture::
05348 do_get_expected_mipmap_z_size(const CData *cdata, int n) const {
05349   // 3-D textures have a different number of pages per each mipmap
05350   // level.  Other kinds of textures--especially, cube map
05351   // textures--always have the same.
05352   if (cdata->_texture_type == Texture::TT_3d_texture) {
05353     int size = max(cdata->_z_size, 1);
05354     while (n > 0 && size > 1) {
05355       size >>= 1;
05356       --n;
05357     }
05358     return size;
05359 
05360   } else {
05361     return cdata->_z_size;
05362   }
05363 }
05364 
05365 ////////////////////////////////////////////////////////////////////
05366 //     Function: Texture::do_clear_simple_ram_image
05367 //       Access: Protected
05368 //  Description:
05369 ////////////////////////////////////////////////////////////////////
05370 void Texture::
05371 do_clear_simple_ram_image(CData *cdata) {
05372   cdata->_simple_x_size = 0;
05373   cdata->_simple_y_size = 0;
05374   cdata->_simple_ram_image._image.clear();
05375   cdata->_simple_ram_image._page_size = 0;
05376   cdata->_simple_image_date_generated = 0;
05377 
05378   // We allow this exception: we update the _simple_image_modified
05379   // here, since no one really cares much about that anyway, and it's
05380   // convenient to do it here.
05381   ++(cdata->_simple_image_modified);
05382 }
05383 
05384 ////////////////////////////////////////////////////////////////////
05385 //     Function: Texture::do_clear_ram_mipmap_images
05386 //       Access: Protected
05387 //  Description:
05388 ////////////////////////////////////////////////////////////////////
05389 void Texture::
05390 do_clear_ram_mipmap_images(CData *cdata) {
05391   if (!cdata->_ram_images.empty()) {
05392     cdata->_ram_images.erase(cdata->_ram_images.begin() + 1, cdata->_ram_images.end());
05393   }
05394 }
05395 
05396 ////////////////////////////////////////////////////////////////////
05397 //     Function: Texture::do_generate_ram_mipmap_images
05398 //       Access: Protected
05399 //  Description:
05400 ////////////////////////////////////////////////////////////////////
05401 void Texture::
05402 do_generate_ram_mipmap_images(CData *cdata) {
05403   nassertv(do_has_ram_image(cdata));
05404   nassertv(cdata->_component_type != T_float);
05405   if (do_get_expected_num_mipmap_levels(cdata) == 1) {
05406     // Don't bother.
05407     return;
05408   }
05409 
05410   RamImage orig_compressed_image;
05411   CompressionMode orig_compression_mode = CM_off;
05412 
05413   if (cdata->_ram_image_compression != CM_off) {
05414     // The RAM image is compressed.  This means we need to uncompress
05415     // it in order to generate mipmap images.  Save the original
05416     // first, to avoid lossy recompression.
05417     orig_compressed_image = cdata->_ram_images[0];
05418     orig_compression_mode = cdata->_ram_image_compression;
05419 
05420     // Now try to get the uncompressed source image.
05421     do_get_uncompressed_ram_image(cdata);
05422 
05423     nassertv(cdata->_ram_image_compression == CM_off);
05424   }
05425 
05426   do_clear_ram_mipmap_images(cdata);
05427 
05428   if (gobj_cat.is_debug()) {
05429     gobj_cat.debug()
05430       << "Generating mipmap levels for " << *this << "\n";
05431   }
05432 
05433   if (cdata->_texture_type == Texture::TT_3d_texture && cdata->_z_size != 1) {
05434     // Eek, a 3-D texture.
05435     int x_size = cdata->_x_size;
05436     int y_size = cdata->_y_size;
05437     int z_size = cdata->_z_size;
05438     int n = 0;
05439     while (x_size > 1 || y_size > 1 || z_size > 1) {
05440       cdata->_ram_images.push_back(RamImage());
05441       do_filter_3d_mipmap_level(cdata, cdata->_ram_images[n + 1], cdata->_ram_images[n],
05442                                 x_size, y_size, z_size);
05443       x_size = max(x_size >> 1, 1);
05444       y_size = max(y_size >> 1, 1);
05445       z_size = max(z_size >> 1, 1);
05446       ++n;
05447     }
05448 
05449   } else {
05450     // A 1-D, 2-D, or cube map texture.
05451     int x_size = cdata->_x_size;
05452     int y_size = cdata->_y_size;
05453     int n = 0;
05454     while (x_size > 1 || y_size > 1) {
05455       cdata->_ram_images.push_back(RamImage());
05456       do_filter_2d_mipmap_pages(cdata, cdata->_ram_images[n + 1], cdata->_ram_images[n],
05457                                 x_size, y_size);
05458       x_size = max(x_size >> 1, 1);
05459       y_size = max(y_size >> 1, 1);
05460       ++n;
05461     }
05462   }
05463 
05464   if (orig_compression_mode != CM_off) {
05465     // Now attempt to recompress the mipmap images according to the
05466     // original compression mode.  We don't need to bother compressing
05467     // the first image (it was already compressed, after all), so
05468     // temporarily remove it from the top of the mipmap stack, and
05469     // compress all of the rest of them instead.
05470     nassertv(cdata->_ram_images.size() > 1);
05471     int l0_x_size = cdata->_x_size;
05472     int l0_y_size = cdata->_y_size;
05473     int l0_z_size = cdata->_z_size;
05474     cdata->_x_size = do_get_expected_mipmap_x_size(cdata, 1);
05475     cdata->_y_size = do_get_expected_mipmap_y_size(cdata, 1);
05476     cdata->_z_size = do_get_expected_mipmap_z_size(cdata, 1);
05477     RamImage uncompressed_image = cdata->_ram_images[0];
05478     cdata->_ram_images.erase(cdata->_ram_images.begin());
05479 
05480     bool success = do_compress_ram_image(cdata, orig_compression_mode, QL_default, NULL);
05481     // Now restore the toplevel image.
05482     if (success) {
05483       cdata->_ram_images.insert(cdata->_ram_images.begin(), orig_compressed_image);
05484     } else {
05485       cdata->_ram_images.insert(cdata->_ram_images.begin(), uncompressed_image);
05486     }
05487     cdata->_x_size = l0_x_size;
05488     cdata->_y_size = l0_y_size;
05489     cdata->_z_size = l0_z_size;
05490   }
05491 }
05492 
05493 ////////////////////////////////////////////////////////////////////
05494 //     Function: Texture::do_set_pad_size
05495 //       Access: Protected
05496 //  Description:
05497 ////////////////////////////////////////////////////////////////////
05498 void Texture::
05499 do_set_pad_size(CData *cdata, int x, int y, int z) {
05500   if (x > cdata->_x_size) {
05501     x = cdata->_x_size;
05502   }
05503   if (y > cdata->_y_size) {
05504     y = cdata->_y_size;
05505   }
05506   if (z > cdata->_z_size) {
05507     z = cdata->_z_size;
05508   }
05509 
05510   cdata->_pad_x_size = x;
05511   cdata->_pad_y_size = y;
05512   cdata->_pad_z_size = z;
05513 }
05514 
05515 ////////////////////////////////////////////////////////////////////
05516 //     Function: Texture::do_can_reload
05517 //       Access: Protected, Virtual
05518 //  Description: Returns true if we can safely call
05519 //               do_reload_ram_image() in order to make the image
05520 //               available, or false if we shouldn't do this (because
05521 //               we know from a priori knowledge that it wouldn't work
05522 //               anyway).
05523 ////////////////////////////////////////////////////////////////////
05524 bool Texture::
05525 do_can_reload(const CData *cdata) const {
05526   return (cdata->_loaded_from_image && !cdata->_fullpath.empty());
05527 }
05528 
05529 ////////////////////////////////////////////////////////////////////
05530 //     Function: Texture::do_reload
05531 //       Access: Protected
05532 //  Description:
05533 ////////////////////////////////////////////////////////////////////
05534 bool Texture::
05535 do_reload(CData *cdata) {
05536   if (do_can_reload(cdata)) {
05537     do_clear_ram_image(cdata);
05538     do_reload_ram_image(cdata, true);
05539     if (do_has_ram_image(cdata)) {
05540       // An explicit call to reload() should increment image_modified.
05541       ++(cdata->_image_modified);
05542       return true;
05543     }
05544     return false;
05545   }
05546 
05547   // We don't have a filename to load from.
05548   return false;
05549 }
05550 
05551 ////////////////////////////////////////////////////////////////////
05552 //     Function: Texture::do_has_bam_rawdata
05553 //       Access: Protected, Virtual
05554 //  Description: Returns true if there is a rawdata image that we have
05555 //               available to write to the bam stream.  For a normal
05556 //               Texture, this is the same thing as
05557 //               do_has_ram_image(), but a movie texture might define
05558 //               it differently.
05559 ////////////////////////////////////////////////////////////////////
05560 bool Texture::
05561 do_has_bam_rawdata(const CData *cdata) const {
05562   return do_has_ram_image(cdata);
05563 }
05564 
05565 ////////////////////////////////////////////////////////////////////
05566 //     Function: Texture::do_get_bam_rawdata
05567 //       Access: Protected, Virtual
05568 //  Description: If do_has_bam_rawdata() returned false, this attempts
05569 //               to reload the rawdata image if possible.
05570 ////////////////////////////////////////////////////////////////////
05571 void Texture::
05572 do_get_bam_rawdata(CData *cdata) {
05573   do_get_ram_image(cdata);
05574 }
05575 
05576 ////////////////////////////////////////////////////////////////////
05577 //     Function: Texture::convert_from_pnmimage
05578 //       Access: Private, Static
05579 //  Description: Internal method to convert pixel data from the
05580 //               indicated PNMImage into the given ram_image.
05581 ////////////////////////////////////////////////////////////////////
05582 void Texture::
05583 convert_from_pnmimage(PTA_uchar &image, size_t page_size, int z,
05584                       const PNMImage &pnmimage,
05585                       int num_components, int component_width) {
05586   int x_size = pnmimage.get_x_size();
05587   int y_size = pnmimage.get_y_size();
05588   xelval maxval = pnmimage.get_maxval();
05589 
05590   bool is_grayscale = (num_components == 1 || num_components == 2);
05591   bool has_alpha = (num_components == 2 || num_components == 4);
05592   bool img_has_alpha = pnmimage.has_alpha();
05593 
05594   int idx = page_size * z;
05595   nassertv(idx + page_size <= image.size());
05596   unsigned char *p = &image[idx];
05597 
05598   if (maxval == 255 && component_width == 1) {
05599     // Most common case: one byte per pixel, and the source image
05600     // shows a maxval of 255.  No scaling is necessary.
05601     for (int j = y_size-1; j >= 0; j--) {
05602       for (int i = 0; i < x_size; i++) {
05603         if (is_grayscale) {
05604           store_unscaled_byte(p, pnmimage.get_gray_val(i, j));
05605         } else {
05606           store_unscaled_byte(p, pnmimage.get_blue_val(i, j));
05607           store_unscaled_byte(p, pnmimage.get_green_val(i, j));
05608           store_unscaled_byte(p, pnmimage.get_red_val(i, j));
05609         }
05610         if (has_alpha) {
05611           if (img_has_alpha) {
05612             store_unscaled_byte(p, pnmimage.get_alpha_val(i, j));
05613           } else {
05614             store_unscaled_byte(p, 255);
05615           }
05616         }
05617       }
05618     }
05619 
05620   } else if (maxval == 65535 && component_width == 2) {
05621     // Another possible case: two bytes per pixel, and the source
05622     // image shows a maxval of 65535.  Again, no scaling is necessary.
05623     for (int j = y_size-1; j >= 0; j--) {
05624       for (int i = 0; i < x_size; i++) {
05625         if (is_grayscale) {
05626           store_unscaled_short(p, pnmimage.get_gray_val(i, j));
05627         } else {
05628           store_unscaled_short(p, pnmimage.get_blue_val(i, j));
05629           store_unscaled_short(p, pnmimage.get_green_val(i, j));
05630           store_unscaled_short(p, pnmimage.get_red_val(i, j));
05631         }
05632         if (has_alpha) {
05633           if (img_has_alpha) {
05634             store_unscaled_short(p, pnmimage.get_alpha_val(i, j));
05635           } else {
05636             store_unscaled_short(p, 65535);
05637           }
05638         }
05639       }
05640     }
05641 
05642   } else if (component_width == 1) {
05643     // A less common case: one byte per pixel, but the maxval is
05644     // something other than 255.  In this case, we should scale the
05645     // pixel values up to the appropriate amount.
05646     double scale = 255.0 / (double)maxval;
05647 
05648     for (int j = y_size-1; j >= 0; j--) {
05649       for (int i = 0; i < x_size; i++) {
05650         if (is_grayscale) {
05651           store_scaled_byte(p, pnmimage.get_gray_val(i, j), scale);
05652         } else {
05653           store_scaled_byte(p, pnmimage.get_blue_val(i, j), scale);
05654           store_scaled_byte(p, pnmimage.get_green_val(i, j), scale);
05655           store_scaled_byte(p, pnmimage.get_red_val(i, j), scale);
05656         }
05657         if (has_alpha) {
05658           if (img_has_alpha) {
05659             store_scaled_byte(p, pnmimage.get_alpha_val(i, j), scale);
05660           } else {
05661             store_unscaled_byte(p, 255);
05662           }
05663         }
05664       }
05665     }
05666 
05667   } else {  // component_width == 2
05668     // Another uncommon case: two bytes per pixel, and the maxval is
05669     // something other than 65535.  Again, we must scale the pixel
05670     // values.
05671     double scale = 65535.0 / (double)maxval;
05672 
05673     for (int j = y_size-1; j >= 0; j--) {
05674       for (int i = 0; i < x_size; i++) {
05675         if (is_grayscale) {
05676           store_scaled_short(p, pnmimage.get_gray_val(i, j), scale);
05677         } else {
05678           store_scaled_short(p, pnmimage.get_blue_val(i, j), scale);
05679           store_scaled_short(p, pnmimage.get_green_val(i, j), scale);
05680           store_scaled_short(p, pnmimage.get_red_val(i, j), scale);
05681         }
05682         if (has_alpha) {
05683           if (img_has_alpha) {
05684             store_scaled_short(p, pnmimage.get_alpha_val(i, j), 1.0);
05685           } else {
05686             store_unscaled_short(p, 65535);
05687           }
05688         }
05689       }
05690     }
05691   }
05692 
05693   nassertv(p == &image[idx] + page_size);
05694 }
05695 
05696 ////////////////////////////////////////////////////////////////////
05697 //     Function: Texture::convert_to_pnmimage
05698 //       Access: Private, Static
05699 //  Description: Internal method to convert pixel data to the
05700 //               indicated PNMImage from the given ram_image.
05701 ////////////////////////////////////////////////////////////////////
05702 bool Texture::
05703 convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
05704                     int num_components, int component_width,
05705                     CPTA_uchar image, size_t page_size, int z) {
05706   pnmimage.clear(x_size, y_size, num_components);
05707   bool has_alpha = pnmimage.has_alpha();
05708   bool is_grayscale = pnmimage.is_grayscale();
05709 
05710   int idx = page_size * z;
05711   nassertr(idx + page_size <= image.size(), false);
05712   const unsigned char *p = &image[idx];
05713 
05714   if (component_width == 1) {
05715     for (int j = y_size-1; j >= 0; j--) {
05716       for (int i = 0; i < x_size; i++) {
05717         if (is_grayscale) {
05718           pnmimage.set_gray(i, j, get_unsigned_byte(p));
05719         } else {
05720           pnmimage.set_blue(i, j, get_unsigned_byte(p));
05721           pnmimage.set_green(i, j, get_unsigned_byte(p));
05722           pnmimage.set_red(i, j, get_unsigned_byte(p));
05723         }
05724         if (has_alpha) {
05725           pnmimage.set_alpha(i, j, get_unsigned_byte(p));
05726         }
05727       }
05728     }
05729 
05730   } else if (component_width == 2) {
05731     for (int j = y_size-1; j >= 0; j--) {
05732       for (int i = 0; i < x_size; i++) {
05733         if (is_grayscale) {
05734           pnmimage.set_gray(i, j, get_unsigned_short(p));
05735         } else {
05736           pnmimage.set_blue(i, j, get_unsigned_short(p));
05737           pnmimage.set_green(i, j, get_unsigned_short(p));
05738           pnmimage.set_red(i, j, get_unsigned_short(p));
05739         }
05740         if (has_alpha) {
05741           pnmimage.set_alpha(i, j, get_unsigned_short(p));
05742         }
05743       }
05744     }
05745 
05746   } else {
05747     return false;
05748   }
05749 
05750   nassertr(p == &image[idx] + page_size, false);
05751   return true;
05752 }
05753 
05754 ////////////////////////////////////////////////////////////////////
05755 //     Function: Texture::read_dds_level_bgr8
05756 //       Access: Private, Static
05757 //  Description: Called by read_dds for a DDS file in BGR8 format.
05758 ////////////////////////////////////////////////////////////////////
05759 PTA_uchar Texture::
05760 read_dds_level_bgr8(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
05761   // This is in order B, G, R.
05762   int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
05763   int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
05764 
05765   size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
05766   size_t row_bytes = x_size * 3;
05767   PTA_uchar image = PTA_uchar::empty_array(size);
05768   for (int y = y_size - 1; y >= 0; --y) {
05769     unsigned char *p = image.p() + y * row_bytes;
05770     nassertr(p + row_bytes <= image.p() + size, PTA_uchar());
05771     in.read((char *)p, row_bytes);
05772   }
05773 
05774   return image;
05775 }
05776 
05777 ////////////////////////////////////////////////////////////////////
05778 //     Function: Texture::read_dds_level_rgb8
05779 //       Access: Private, Static
05780 //  Description: Called by read_dds for a DDS file in RGB8 format.
05781 ////////////////////////////////////////////////////////////////////
05782 PTA_uchar Texture::
05783 read_dds_level_rgb8(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
05784   // This is in order R, G, B.
05785   int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
05786   int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
05787 
05788   size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
05789   size_t row_bytes = x_size * 3;
05790   PTA_uchar image = PTA_uchar::empty_array(size);
05791   for (int y = y_size - 1; y >= 0; --y) {
05792     unsigned char *p = image.p() + y * row_bytes;
05793     nassertr(p + row_bytes <= image.p() + size, PTA_uchar());
05794     in.read((char *)p, row_bytes);
05795 
05796     // Now reverse the r, g, b triples.
05797     for (int x = 0; x < x_size; ++x) {
05798       unsigned char r = p[0];
05799       p[0] = p[2];
05800       p[2] = r;
05801       p += 3;
05802     }
05803     nassertr(p <= image.p() + size, PTA_uchar());
05804   }
05805 
05806   return image;
05807 }
05808 
05809 ////////////////////////////////////////////////////////////////////
05810 //     Function: Texture::read_dds_level_abgr8
05811 //       Access: Private, Static
05812 //  Description: Called by read_dds for a DDS file in ABGR8 format.
05813 ////////////////////////////////////////////////////////////////////
05814 PTA_uchar Texture::
05815 read_dds_level_abgr8(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
05816   // This is laid out in order R, G, B, A.
05817   int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
05818   int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
05819 
05820   size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
05821   size_t row_bytes = x_size * 4;
05822   PTA_uchar image = PTA_uchar::empty_array(size);
05823   for (int y = y_size - 1; y >= 0; --y) {
05824     unsigned char *p = image.p() + y * row_bytes;
05825     in.read((char *)p, row_bytes);
05826 
05827     PN_uint32 *pw = (PN_uint32 *)p;
05828     for (int x = 0; x < x_size; ++x) {
05829       PN_uint32 w = *pw;
05830 #ifdef WORDS_BIGENDIAN
05831       // bigendian: convert R, G, B, A to B, G, R, A.
05832       w = ((w & 0xff00) << 16) | ((w & 0xff000000U) >> 16) | (w & 0xff00ff);
05833 #else
05834       // littendian: convert A, B, G, R to to A, R, G, B.
05835       w = ((w & 0xff) << 16) | ((w & 0xff0000) >> 16) | (w & 0xff00ff00U);
05836 #endif
05837       *pw = w;
05838       ++pw;
05839     }
05840     nassertr((unsigned char *)pw <= image.p() + size, PTA_uchar());
05841   }
05842 
05843   return image;
05844 }
05845 
05846 ////////////////////////////////////////////////////////////////////
05847 //     Function: Texture::read_dds_level_rgba8
05848 //       Access: Private, Static
05849 //  Description: Called by read_dds for a DDS file in RGBA8 format.
05850 ////////////////////////////////////////////////////////////////////
05851 PTA_uchar Texture::
05852 read_dds_level_rgba8(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
05853   // This is actually laid out in order B, G, R, A.
05854   int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
05855   int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
05856 
05857   size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
05858   size_t row_bytes = x_size * 4;
05859   PTA_uchar image = PTA_uchar::empty_array(size);
05860   for (int y = y_size - 1; y >= 0; --y) {
05861     unsigned char *p = image.p() + y * row_bytes;
05862     nassertr(p + row_bytes <= image.p() + size, PTA_uchar());
05863     in.read((char *)p, row_bytes);
05864   }
05865 
05866   return image;
05867 }
05868 
05869 ////////////////////////////////////////////////////////////////////
05870 //     Function: Texture::read_dds_level_generic_uncompressed
05871 //       Access: Private, Static
05872 //  Description: Called by read_dds for a DDS file whose format isn't
05873 //               one we've specifically optimized.
05874 ////////////////////////////////////////////////////////////////////
05875 PTA_uchar Texture::
05876 read_dds_level_generic_uncompressed(Texture *tex, CData *cdata, const DDSHeader &header,
05877                                     int n, istream &in) {
05878   int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
05879   int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
05880 
05881   int pitch = (x_size * header.pf.rgb_bitcount) / 8;
05882 
05883   // MS says the pitch can be supplied in the header file and must be
05884   // DWORD aligned, but this appears to apply to level 0 mipmaps only
05885   // (where it almost always will be anyway).  Other mipmap levels
05886   // seem to be tightly packed, but there isn't a separate pitch for
05887   // each mipmap level.  Weird.
05888   if (n == 0) {
05889     pitch = ((pitch + 3) / 4) * 4;
05890     if (header.dds_flags & DDSD_PITCH) {
05891       pitch = header.pitch;
05892     }
05893   }
05894 
05895   int bpp = header.pf.rgb_bitcount / 8;
05896   int skip_bytes = pitch - (bpp * x_size);
05897   nassertr(skip_bytes >= 0, PTA_uchar());
05898 
05899   unsigned int r_mask = header.pf.r_mask;
05900   unsigned int g_mask = header.pf.g_mask;
05901   unsigned int b_mask = header.pf.b_mask;
05902   unsigned int a_mask = header.pf.a_mask;
05903 
05904   // Determine the number of bits to shift each mask to the right so
05905   // that the lowest on bit is at bit 0.
05906   int r_shift = get_lowest_on_bit(r_mask);
05907   int g_shift = get_lowest_on_bit(g_mask);
05908   int b_shift = get_lowest_on_bit(b_mask);
05909   int a_shift = get_lowest_on_bit(a_mask);
05910 
05911   // Then determine the scale factor required to raise the highest
05912   // color value to 0xff000000.
05913   unsigned int r_scale = 0;
05914   if (r_mask != 0) {
05915     r_scale = 0xff000000 / (r_mask >> r_shift);
05916   }
05917   unsigned int g_scale = 0;
05918   if (g_mask != 0) {
05919     g_scale = 0xff000000 / (g_mask >> g_shift);
05920   }
05921   unsigned int b_scale = 0;
05922   if (b_mask != 0) {
05923     b_scale = 0xff000000 / (b_mask >> b_shift);
05924   }
05925   unsigned int a_scale = 0;
05926   if (a_mask != 0) {
05927     a_scale = 0xff000000 / (a_mask >> a_shift);
05928   }
05929 
05930   bool add_alpha = has_alpha(cdata->_format);
05931 
05932   size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
05933   size_t row_bytes = x_size * cdata->_num_components;
05934   PTA_uchar image = PTA_uchar::empty_array(size);
05935   for (int y = y_size - 1; y >= 0; --y) {
05936     unsigned char *p = image.p() + y * row_bytes;
05937     for (int x = 0; x < x_size; ++x) {
05938 
05939       // Read a little-endian numeric value of bpp bytes.
05940       unsigned int pixel = 0;
05941       int shift = 0;
05942       for (int bi = 0; bi < bpp; ++bi) {
05943         unsigned int ch = (unsigned char)in.get();
05944         pixel |= (ch << shift);
05945         shift += 8;
05946       }
05947 
05948       // Then break apart that value into its R, G, B, and maybe A
05949       // components.
05950       unsigned int r = (((pixel & r_mask) >> r_shift) * r_scale) >> 24;
05951       unsigned int g = (((pixel & g_mask) >> g_shift) * g_scale) >> 24;
05952       unsigned int b = (((pixel & b_mask) >> b_shift) * b_scale) >> 24;
05953 
05954       // Store the components in the Texture's image data.
05955       store_unscaled_byte(p, b);
05956       store_unscaled_byte(p, g);
05957       store_unscaled_byte(p, r);
05958       if (add_alpha) {
05959         unsigned int a = (((pixel & a_mask) >> a_shift) * a_scale) >> 24;
05960         store_unscaled_byte(p, a);
05961       }
05962     }
05963     nassertr(p <= image.p() + size, PTA_uchar());
05964     for (int bi = 0; bi < skip_bytes; ++bi) {
05965       in.get();
05966     }
05967   }
05968 
05969   return image;
05970 }
05971 
05972 ////////////////////////////////////////////////////////////////////
05973 //     Function: Texture::read_dds_level_luminance_uncompressed
05974 //       Access: Private, Static
05975 //  Description: Called by read_dds for a DDS file in uncompressed
05976 //               luminance or luminance-alpha format.
05977 ////////////////////////////////////////////////////////////////////
05978 PTA_uchar Texture::
05979 read_dds_level_luminance_uncompressed(Texture *tex, CData *cdata, const DDSHeader &header,
05980                                       int n, istream &in) {
05981   int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
05982   int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
05983 
05984   int pitch = (x_size * header.pf.rgb_bitcount) / 8;
05985 
05986   // MS says the pitch can be supplied in the header file and must be
05987   // DWORD aligned, but this appears to apply to level 0 mipmaps only
05988   // (where it almost always will be anyway).  Other mipmap levels
05989   // seem to be tightly packed, but there isn't a separate pitch for
05990   // each mipmap level.  Weird.
05991   if (n == 0) {
05992     pitch = ((pitch + 3) / 4) * 4;
05993     if (header.dds_flags & DDSD_PITCH) {
05994       pitch = header.pitch;
05995     }
05996   }
05997 
05998   int bpp = header.pf.rgb_bitcount / 8;
05999   int skip_bytes = pitch - (bpp * x_size);
06000   nassertr(skip_bytes >= 0, PTA_uchar());
06001 
06002   unsigned int r_mask = header.pf.r_mask;
06003   unsigned int a_mask = header.pf.a_mask;
06004 
06005   // Determine the number of bits to shift each mask to the right so
06006   // that the lowest on bit is at bit 0.
06007   int r_shift = get_lowest_on_bit(r_mask);
06008   int a_shift = get_lowest_on_bit(a_mask);
06009 
06010   // Then determine the scale factor required to raise the highest
06011   // color value to 0xff000000.
06012   unsigned int r_scale = 0;
06013   if (r_mask != 0) {
06014     r_scale = 0xff000000 / (r_mask >> r_shift);
06015   }
06016   unsigned int a_scale = 0;
06017   if (a_mask != 0) {
06018     a_scale = 0xff000000 / (a_mask >> a_shift);
06019   }
06020 
06021   bool add_alpha = has_alpha(cdata->_format);
06022 
06023   size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
06024   size_t row_bytes = x_size * cdata->_num_components;
06025   PTA_uchar image = PTA_uchar::empty_array(size);
06026   for (int y = y_size - 1; y >= 0; --y) {
06027     unsigned char *p = image.p() + y * row_bytes;
06028     for (int x = 0; x < x_size; ++x) {
06029 
06030       // Read a little-endian numeric value of bpp bytes.
06031       unsigned int pixel = 0;
06032       int shift = 0;
06033       for (int bi = 0; bi < bpp; ++bi) {
06034         unsigned int ch = (unsigned char)in.get();
06035         pixel |= (ch << shift);
06036         shift += 8;
06037       }
06038 
06039       unsigned int r = (((pixel & r_mask) >> r_shift) * r_scale) >> 24;
06040 
06041       // Store the components in the Texture's image data.
06042       store_unscaled_byte(p, r);
06043       if (add_alpha) {
06044         unsigned int a = (((pixel & a_mask) >> a_shift) * a_scale) >> 24;
06045         store_unscaled_byte(p, a);
06046       }
06047     }
06048     nassertr(p <= image.p() + size, PTA_uchar());
06049     for (int bi = 0; bi < skip_bytes; ++bi) {
06050       in.get();
06051     }
06052   }
06053 
06054   return image;
06055 }
06056 
06057 ////////////////////////////////////////////////////////////////////
06058 //     Function: Texture::read_dds_level_dxt1
06059 //       Access: Private, Static
06060 //  Description: Called by read_dds for DXT1 file format.
06061 ////////////////////////////////////////////////////////////////////
06062 PTA_uchar Texture::
06063 read_dds_level_dxt1(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
06064   int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
06065   int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
06066 
06067   static const int div = 4;
06068   static const int block_bytes = 8;
06069 
06070   // The DXT1 image is divided into num_rows x num_cols blocks, where
06071   // each block represents 4x4 pixels.
06072   int num_cols = max(div, x_size) / div;
06073   int num_rows = max(div, y_size) / div;
06074   int row_length = num_cols * block_bytes;
06075   int linear_size = row_length * num_rows;
06076 
06077   if (n == 0) {
06078     if (header.dds_flags & DDSD_LINEARSIZE) {
06079       nassertr(linear_size == (int)header.pitch, PTA_uchar());
06080     }
06081   }
06082 
06083   PTA_uchar image = PTA_uchar::empty_array(linear_size);
06084 
06085   if (y_size >= 4) {
06086     // We have to flip the image as we read it, because of DirectX's
06087     // inverted sense of up.  That means we (a) reverse the order of the
06088     // rows of blocks . . .
06089     for (int ri = num_rows - 1; ri >= 0; --ri) {
06090       unsigned char *p = image.p() + row_length * ri;
06091       in.read((char *)p, row_length);
06092 
06093       for (int ci = 0; ci < num_cols; ++ci) {
06094         // . . . and (b) within each block, we reverse the 4 individual
06095         // rows of 4 pixels.
06096         PN_uint32 *cells = (PN_uint32 *)p;
06097         PN_uint32 w = cells[1];
06098         w = ((w & 0xff) << 24) | ((w & 0xff00) << 8) | ((w & 0xff0000) >> 8) | ((w & 0xff000000U) >> 24);
06099         cells[1] = w;
06100 
06101         p += block_bytes;
06102       }
06103     }
06104 
06105   } else if (y_size >= 2) {
06106     // To invert a two-pixel high image, we just flip two rows within a cell.
06107     unsigned char *p = image.p();
06108     in.read((char *)p, row_length);
06109 
06110     for (int ci = 0; ci < num_cols; ++ci) {
06111       PN_uint32 *cells = (PN_uint32 *)p;
06112       PN_uint32 w = cells[1];
06113       w = ((w & 0xff) << 8) | ((w & 0xff00) >> 8);
06114       cells[1] = w;
06115 
06116       p += block_bytes;
06117     }
06118 
06119   } else if (y_size >= 1) {
06120     // No need to invert a one-pixel-high image.
06121     unsigned char *p = image.p();
06122     in.read((char *)p, row_length);
06123   }
06124 
06125   return image;
06126 }
06127 
06128 ////////////////////////////////////////////////////////////////////
06129 //     Function: Texture::read_dds_level_dxt23
06130 //       Access: Private, Static
06131 //  Description: Called by read_dds for DXT2 or DXT3 file format.
06132 ////////////////////////////////////////////////////////////////////
06133 PTA_uchar Texture::
06134 read_dds_level_dxt23(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
06135   int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
06136   int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
06137 
06138   static const int div = 4;
06139   static const int block_bytes = 16;
06140 
06141   // The DXT3 image is divided into num_rows x num_cols blocks, where
06142   // each block represents 4x4 pixels.  Unlike DXT1, each block
06143   // consists of two 8-byte chunks, representing the alpha and color
06144   // separately.
06145   int num_cols = max(div, x_size) / div;
06146   int num_rows = max(div, y_size) / div;
06147   int row_length = num_cols * block_bytes;
06148   int linear_size = row_length * num_rows;
06149 
06150   if (n == 0) {
06151     if (header.dds_flags & DDSD_LINEARSIZE) {
06152       nassertr(linear_size == (int)header.pitch, PTA_uchar());
06153     }
06154   }
06155 
06156   PTA_uchar image = PTA_uchar::empty_array(linear_size);
06157 
06158   if (y_size >= 4) {
06159     // We have to flip the image as we read it, because of DirectX's
06160     // inverted sense of up.  That means we (a) reverse the order of the
06161     // rows of blocks . . .
06162     for (int ri = num_rows - 1; ri >= 0; --ri) {
06163       unsigned char *p = image.p() + row_length * ri;
06164       in.read((char *)p, row_length);
06165 
06166       for (int ci = 0; ci < num_cols; ++ci) {
06167         // . . . and (b) within each block, we reverse the 4 individual
06168         // rows of 4 pixels.
06169         PN_uint32 *cells = (PN_uint32 *)p;
06170 
06171         // Alpha.  The block is four 16-bit words of pixel data.
06172         PN_uint32 w0 = cells[0];
06173         PN_uint32 w1 = cells[1];
06174         w0 = ((w0 & 0xffff) << 16) | ((w0 & 0xffff0000U) >> 16);
06175         w1 = ((w1 & 0xffff) << 16) | ((w1 & 0xffff0000U) >> 16);
06176         cells[0] = w1;
06177         cells[1] = w0;
06178 
06179         // Color.  Only the second 32-bit dword of the color block
06180         // represents the pixel data.
06181         PN_uint32 w = cells[3];
06182         w = ((w & 0xff) << 24) | ((w & 0xff00) << 8) | ((w & 0xff0000) >> 8) | ((w & 0xff000000U) >> 24);
06183         cells[3] = w;
06184 
06185         p += block_bytes;
06186       }
06187     }
06188 
06189   } else if (y_size >= 2) {
06190     // To invert a two-pixel high image, we just flip two rows within a cell.
06191     unsigned char *p = image.p();
06192     in.read((char *)p, row_length);
06193 
06194     for (int ci = 0; ci < num_cols; ++ci) {
06195       PN_uint32 *cells = (PN_uint32 *)p;
06196 
06197       PN_uint32 w0 = cells[0];
06198       w0 = ((w0 & 0xffff) << 16) | ((w0 & 0xffff0000U) >> 16);
06199       cells[0] = w0;
06200 
06201       PN_uint32 w = cells[3];
06202       w = ((w & 0xff) << 8) | ((w & 0xff00) >> 8);
06203       cells[3] = w;
06204 
06205       p += block_bytes;
06206     }
06207 
06208   } else if (y_size >= 1) {
06209     // No need to invert a one-pixel-high image.
06210     unsigned char *p = image.p();
06211     in.read((char *)p, row_length);
06212   }
06213 
06214   return image;
06215 }
06216 
06217 ////////////////////////////////////////////////////////////////////
06218 //     Function: Texture::read_dds_level_dxt45
06219 //       Access: Private, Static
06220 //  Description: Called by read_dds for DXT4 or DXT5 file format.
06221 ////////////////////////////////////////////////////////////////////
06222 PTA_uchar Texture::
06223 read_dds_level_dxt45(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
06224   int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
06225   int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
06226 
06227   static const int div = 4;
06228   static const int block_bytes = 16;
06229 
06230   // The DXT5 image is similar to DXT3, in that there each 4x4 block
06231   // of pixels consists of an alpha block and a color block, but the
06232   // layout of the alpha block is different.
06233   int num_cols = max(div, x_size) / div;
06234   int num_rows = max(div, y_size) / div;
06235   int row_length = num_cols * block_bytes;
06236   int linear_size = row_length * num_rows;
06237 
06238   if (n == 0) {
06239     if (header.dds_flags & DDSD_LINEARSIZE) {
06240       nassertr(linear_size == (int)header.pitch, PTA_uchar());
06241     }
06242   }
06243 
06244   PTA_uchar image = PTA_uchar::empty_array(linear_size);
06245 
06246   if (y_size >= 4) {
06247     // We have to flip the image as we read it, because of DirectX's
06248     // inverted sense of up.  That means we (a) reverse the order of the
06249     // rows of blocks . . .
06250     for (int ri = num_rows - 1; ri >= 0; --ri) {
06251       unsigned char *p = image.p() + row_length * ri;
06252       in.read((char *)p, row_length);
06253 
06254       for (int ci = 0; ci < num_cols; ++ci) {
06255         // . . . and (b) within each block, we reverse the 4 individual
06256         // rows of 4 pixels.
06257         PN_uint32 *cells = (PN_uint32 *)p;
06258 
06259         // Alpha.  The block is one 16-bit word of reference values,
06260         // followed by six words of pixel values, in 12-bit rows.
06261         // Tricky to invert.
06262         unsigned char p2 = p[2];
06263         unsigned char p3 = p[3];
06264         unsigned char p4 = p[4];
06265         unsigned char p5 = p[5];
06266         unsigned char p6 = p[6];
06267         unsigned char p7 = p[7];
06268 
06269         p[2] = ((p7 & 0xf) << 4) | ((p6 & 0xf0) >> 4);
06270         p[3] = ((p5 & 0xf) << 4) | ((p7 & 0xf0) >> 4);
06271         p[4] = ((p6 & 0xf) << 4) | ((p5 & 0xf0) >> 4);
06272         p[5] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4);
06273         p[6] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4);
06274         p[7] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4);
06275 
06276         // Color.  Only the second 32-bit dword of the color block
06277         // represents the pixel data.
06278         PN_uint32 w = cells[3];
06279         w = ((w & 0xff) << 24) | ((w & 0xff00) << 8) | ((w & 0xff0000) >> 8) | ((w & 0xff000000U) >> 24);
06280         cells[3] = w;
06281 
06282         p += block_bytes;
06283       }
06284     }
06285 
06286   } else if (y_size >= 2) {
06287     // To invert a two-pixel high image, we just flip two rows within a cell.
06288     unsigned char *p = image.p();
06289     in.read((char *)p, row_length);
06290 
06291     for (int ci = 0; ci < num_cols; ++ci) {
06292       PN_uint32 *cells = (PN_uint32 *)p;
06293 
06294       unsigned char p2 = p[2];
06295       unsigned char p3 = p[3];
06296       unsigned char p4 = p[4];
06297 
06298       p[2] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4);
06299       p[3] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4);
06300       p[4] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4);
06301 
06302       PN_uint32 w0 = cells[0];
06303       w0 = ((w0 & 0xffff) << 16) | ((w0 & 0xffff0000U) >> 16);
06304       cells[0] = w0;
06305 
06306       PN_uint32 w = cells[3];
06307       w = ((w & 0xff) << 8) | ((w & 0xff00) >> 8);
06308       cells[3] = w;
06309 
06310       p += block_bytes;
06311     }
06312 
06313   } else if (y_size >= 1) {
06314     // No need to invert a one-pixel-high image.
06315     unsigned char *p = image.p();
06316     in.read((char *)p, row_length);
06317   }
06318 
06319   return image;
06320 }
06321 
06322 ////////////////////////////////////////////////////////////////////
06323 //     Function: Texture::clear_prepared
06324 //       Access: Private
06325 //  Description: Removes the indicated PreparedGraphicsObjects table
06326 //               from the Texture's table, without actually releasing
06327 //               the texture.  This is intended to be called only from
06328 //               PreparedGraphicsObjects::release_texture(); it should
06329 //               never be called by user code.
06330 ////////////////////////////////////////////////////////////////////
06331 void Texture::
06332 clear_prepared(int view, PreparedGraphicsObjects *prepared_objects) {
06333   PreparedViews::iterator pvi;
06334   pvi = _prepared_views.find(prepared_objects);
06335   if (pvi != _prepared_views.end()) {
06336     Contexts &contexts = (*pvi).second;
06337     Contexts::iterator ci;
06338     ci = contexts.find(view);
06339     if (ci != contexts.end()) {
06340       contexts.erase(ci);
06341     }
06342 
06343     if (contexts.empty()) {
06344       _prepared_views.erase(pvi);
06345     }
06346   }
06347 }
06348 
06349 ////////////////////////////////////////////////////////////////////
06350 //     Function: Texture::consider_downgrade
06351 //       Access: Private, Static
06352 //  Description: Reduces the number of channels in the texture, if
06353 //               necessary, according to num_channels.
06354 ////////////////////////////////////////////////////////////////////
06355 void Texture::
06356 consider_downgrade(PNMImage &pnmimage, int num_channels, const string &name) {
06357   if (num_channels != 0 && num_channels < pnmimage.get_num_channels()) {
06358     // One special case: we can't reduce from 3 to 2 components, since
06359     // that would require adding an alpha channel.
06360     if (pnmimage.get_num_channels() == 3 && num_channels == 2) {
06361       return;
06362     }
06363 
06364     gobj_cat.info()
06365       << "Downgrading " << name << " from "
06366       << pnmimage.get_num_channels() << " components to "
06367       << num_channels << ".\n";
06368     pnmimage.set_num_channels(num_channels);
06369   }
06370 }
06371 
06372 ////////////////////////////////////////////////////////////////////
06373 //     Function: Texture::compare_images
06374 //       Access: Private, Static
06375 //  Description: Called by generate_simple_ram_image(), this compares
06376 //               the two PNMImages pixel-by-pixel.  If they're similar
06377 //               enough (within a given threshold), returns true.
06378 ////////////////////////////////////////////////////////////////////
06379 bool Texture::
06380 compare_images(const PNMImage &a, const PNMImage &b) {
06381   nassertr(a.get_maxval() == 255 && b.get_maxval() == 255, false);
06382   nassertr(a.get_num_channels() == 4 && b.get_num_channels() == 4, false);
06383   nassertr(a.get_x_size() == b.get_x_size() &&
06384            a.get_y_size() == b.get_y_size(), false);
06385 
06386   int delta = 0;
06387   for (int yi = 0; yi < a.get_y_size(); ++yi) {
06388     for (int xi = 0; xi < a.get_x_size(); ++xi) {
06389       delta += abs(a.get_red_val(xi, yi) - b.get_red_val(xi, yi));
06390       delta += abs(a.get_green_val(xi, yi) - b.get_green_val(xi, yi));
06391       delta += abs(a.get_blue_val(xi, yi) - b.get_blue_val(xi, yi));
06392       delta += abs(a.get_alpha_val(xi, yi) - b.get_alpha_val(xi, yi));
06393     }
06394   }
06395 
06396   double average_delta = (double)delta / ((double)a.get_x_size() * (double)b.get_y_size() * (double)a.get_maxval());
06397   return (average_delta <= simple_image_threshold);
06398 }
06399 
06400 ////////////////////////////////////////////////////////////////////
06401 //     Function: Texture::do_filter_2d_mipmap_pages
06402 //       Access: Private
06403 //  Description: Generates the next mipmap level from the previous
06404 //               one.  If there are multiple pages (e.g. a cube map),
06405 //               generates each page independently.
06406 //
06407 //               x_size and y_size are the size of the previous level.
06408 //               They need not be a power of 2, or even a multiple of
06409 //               2.
06410 //
06411 //               Assumes the lock is already held.
06412 ////////////////////////////////////////////////////////////////////
06413 void Texture::
06414 do_filter_2d_mipmap_pages(const CData *cdata,
06415                           Texture::RamImage &to, const Texture::RamImage &from,
06416                           int x_size, int y_size) const {
06417   size_t pixel_size = cdata->_num_components * cdata->_component_width;
06418   size_t row_size = (size_t)x_size * pixel_size;
06419 
06420   int to_x_size = max(x_size >> 1, 1);
06421   int to_y_size = max(y_size >> 1, 1);
06422 
06423   size_t to_row_size = (size_t)to_x_size * pixel_size;
06424   to._page_size = (size_t)to_y_size * to_row_size;
06425   to._image = PTA_uchar::empty_array(to._page_size * cdata->_z_size * cdata->_num_views, get_class_type());
06426 
06427   Filter2DComponent *filter_component = (cdata->_component_type == T_unsigned_byte ? &filter_2d_unsigned_byte : filter_2d_unsigned_short);
06428 
06429   int num_pages = cdata->_z_size * cdata->_num_views;
06430   for (int z = 0; z < num_pages; ++z) {
06431     // For each level.
06432     unsigned char *p = to._image.p() + z * to._page_size;
06433     nassertv(p <= to._image.p() + to._image.size() + to._page_size);
06434     const unsigned char *q = from._image.p() + z * from._page_size;
06435     nassertv(q <= from._image.p() + from._image.size() + from._page_size);
06436     if (y_size != 1) {
06437       int y;
06438       for (y = 0; y < y_size - 1; y += 2) {
06439         // For each row.
06440         nassertv(p == to._image.p() + z * to._page_size + (y / 2) * to_row_size);
06441         nassertv(q == from._image.p() + z * from._page_size + y * row_size);
06442         if (x_size != 1) {
06443           int x;
06444           for (x = 0; x < x_size - 1; x += 2) {
06445             // For each pixel.
06446             for (int c = 0; c < cdata->_num_components; ++c) {
06447               // For each component.
06448               filter_component(p, q, pixel_size, row_size);
06449             }
06450             q += pixel_size;
06451           }
06452           if (x < x_size) {
06453             // Skip the last odd pixel.
06454             q += pixel_size;
06455           }
06456         } else {
06457           // Just one pixel.
06458           for (int c = 0; c < cdata->_num_components; ++c) {
06459             // For each component.
06460             filter_component(p, q, 0, row_size);
06461           }
06462         }
06463         q += row_size;
06464         Thread::consider_yield();
06465       }
06466       if (y < y_size) {
06467         // Skip the last odd row.
06468         q += row_size;
06469       }
06470     } else {
06471       // Just one row.
06472       if (x_size != 1) {
06473         int x;
06474         for (x = 0; x < x_size - 1; x += 2) {
06475           // For each pixel.
06476           for (int c = 0; c < cdata->_num_components; ++c) {
06477             // For each component.
06478             filter_component(p, q, pixel_size, 0);
06479           }
06480           q += pixel_size;
06481         }
06482         if (x < x_size) {
06483           // Skip the last odd pixel.
06484           q += pixel_size;
06485         }
06486       } else {
06487         // Just one pixel.
06488         for (int c = 0; c < cdata->_num_components; ++c) {
06489           // For each component.
06490           filter_component(p, q, 0, 0);
06491         }
06492       }
06493     }
06494 
06495     nassertv(p == to._image.p() + (z + 1) * to._page_size);
06496     nassertv(q == from._image.p() + (z + 1) * from._page_size);
06497   }
06498 }
06499 
06500 ////////////////////////////////////////////////////////////////////
06501 //     Function: Texture::do_filter_3d_mipmap_level
06502 //       Access: Private
06503 //  Description: Generates the next mipmap level from the previous
06504 //               one, treating all the pages of the level as a single
06505 //               3-d block of pixels.
06506 //
06507 //               x_size, y_size, and z_size are the size of the
06508 //               previous level.  They need not be a power of 2, or
06509 //               even a multiple of 2.
06510 //
06511 //               Assumes the lock is already held.
06512 ////////////////////////////////////////////////////////////////////
06513 void Texture::
06514 do_filter_3d_mipmap_level(const CData *cdata,
06515                           Texture::RamImage &to, const Texture::RamImage &from,
06516                           int x_size, int y_size, int z_size) const {
06517   size_t pixel_size = cdata->_num_components * cdata->_component_width;
06518   size_t row_size = (size_t)x_size * pixel_size;
06519   size_t page_size = (size_t)y_size * row_size;
06520   size_t view_size = (size_t)z_size * page_size;
06521 
06522   int to_x_size = max(x_size >> 1, 1);
06523   int to_y_size = max(y_size >> 1, 1);
06524   int to_z_size = max(z_size >> 1, 1);
06525 
06526   size_t to_row_size = (size_t)to_x_size * pixel_size;
06527   size_t to_page_size = (size_t)to_y_size * to_row_size;
06528   size_t to_view_size = (size_t)to_z_size * to_page_size;
06529   to._page_size = to_page_size;
06530   to._image = PTA_uchar::empty_array(to_page_size * to_z_size * cdata->_num_views, get_class_type());
06531 
06532   Filter3DComponent *filter_component = (cdata->_component_type == T_unsigned_byte ? &filter_3d_unsigned_byte : filter_3d_unsigned_short);
06533 
06534   for (int view = 0; view < cdata->_num_views; ++view) {
06535     unsigned char *start_to = to._image.p() + view * to_view_size;
06536     const unsigned char *start_from = from._image.p() + view * view_size;
06537     nassertv(start_to + to_view_size <= to._image.p() + to._image.size());
06538     nassertv(start_from + view_size <= from._image.p() + from._image.size());
06539     unsigned char *p = start_to;
06540     const unsigned char *q = start_from;
06541     if (z_size != 1) {
06542       int z;
06543       for (z = 0; z < z_size - 1; z += 2) {
06544         // For each level.
06545         nassertv(p == start_to + (z / 2) * to_page_size);
06546         nassertv(q == start_from + z * page_size);
06547         if (y_size != 1) {
06548           int y;
06549           for (y = 0; y < y_size - 1; y += 2) {
06550             // For each row.
06551             nassertv(p == start_to + (z / 2) * to_page_size + (y / 2) * to_row_size);
06552             nassertv(q == start_from + z * page_size + y * row_size);
06553             if (x_size != 1) {
06554               int x;
06555               for (x = 0; x < x_size - 1; x += 2) {
06556                 // For each pixel.
06557                 for (int c = 0; c < cdata->_num_components; ++c) {
06558                   // For each component.
06559                   filter_component(p, q, pixel_size, row_size, page_size);
06560                 }
06561                 q += pixel_size;
06562               }
06563               if (x < x_size) {
06564                 // Skip the last odd pixel.
06565                 q += pixel_size;
06566               }
06567             } else {
06568               // Just one pixel.
06569               for (int c = 0; c < cdata->_num_components; ++c) {
06570                 // For each component.
06571                 filter_component(p, q, 0, row_size, page_size);
06572               }
06573             }
06574             q += row_size;
06575             Thread::consider_yield();
06576           }
06577           if (y < y_size) {
06578             // Skip the last odd row.
06579             q += row_size;
06580           }
06581         } else {
06582           // Just one row.
06583           if (x_size != 1) {
06584             int x;
06585             for (x = 0; x < x_size - 1; x += 2) {
06586               // For each pixel.
06587               for (int c = 0; c < cdata->_num_components; ++c) {
06588                 // For each component.
06589                 filter_component(p, q, pixel_size, 0, page_size);
06590               }
06591               q += pixel_size;
06592             }
06593             if (x < x_size) {
06594               // Skip the last odd pixel.
06595               q += pixel_size;
06596             }
06597           } else {
06598             // Just one pixel.
06599             for (int c = 0; c < cdata->_num_components; ++c) {
06600               // For each component.
06601               filter_component(p, q, 0, 0, page_size);
06602             }
06603           }
06604         }
06605         q += page_size;
06606       }
06607       if (z < z_size) {
06608         // Skip the last odd page.
06609         q += page_size;
06610       }
06611     } else {
06612       // Just one page.
06613       if (y_size != 1) {
06614         int y;
06615         for (y = 0; y < y_size - 1; y += 2) {
06616           // For each row.
06617           nassertv(p == start_to + (y / 2) * to_row_size);
06618           nassertv(q == start_from + y * row_size);
06619           if (x_size != 1) {
06620             int x;
06621             for (x = 0; x < x_size - 1; x += 2) {
06622               // For each pixel.
06623               for (int c = 0; c < cdata->_num_components; ++c) {
06624                 // For each component.
06625                 filter_component(p, q, pixel_size, row_size, 0);
06626               }
06627               q += pixel_size;
06628             }
06629             if (x < x_size) {
06630               // Skip the last odd pixel.
06631               q += pixel_size;
06632             }
06633           } else {
06634             // Just one pixel.
06635             for (int c = 0; c < cdata->_num_components; ++c) {
06636               // For each component.
06637               filter_component(p, q, 0, row_size, 0);
06638             }
06639           }
06640           q += row_size;
06641           Thread::consider_yield();
06642         }
06643         if (y < y_size) {
06644           // Skip the last odd row.
06645           q += row_size;
06646         }
06647       } else {
06648         // Just one row.
06649         if (x_size != 1) {
06650           int x;
06651           for (x = 0; x < x_size - 1; x += 2) {
06652             // For each pixel.
06653             for (int c = 0; c < cdata->_num_components; ++c) {
06654               // For each component.
06655               filter_component(p, q, pixel_size, 0, 0);
06656             }
06657             q += pixel_size;
06658           }
06659           if (x < x_size) {
06660             // Skip the last odd pixel.
06661             q += pixel_size;
06662           }
06663         } else {
06664           // Just one pixel.
06665           for (int c = 0; c < cdata->_num_components; ++c) {
06666             // For each component.
06667             filter_component(p, q, 0, 0, 0);
06668           }
06669         }
06670       }
06671     }
06672 
06673     nassertv(p == start_to + to_z_size * to_page_size);
06674     nassertv(q == start_from + z_size * page_size);
06675   }
06676 }
06677 
06678 ////////////////////////////////////////////////////////////////////
06679 //     Function: Texture::filter_2d_unsigned_byte
06680 //       Access: Public, Static
06681 //  Description: Averages a 2x2 block of pixel components into a
06682 //               single pixel component, for producing the next mipmap
06683 //               level.  Increments p and q to the next component.
06684 ////////////////////////////////////////////////////////////////////
06685 void Texture::
06686 filter_2d_unsigned_byte(unsigned char *&p, const unsigned char *&q,
06687                         size_t pixel_size, size_t row_size) {
06688   unsigned int result = ((unsigned int)q[0] +
06689                          (unsigned int)q[pixel_size] +
06690                          (unsigned int)q[row_size] +
06691                          (unsigned int)q[pixel_size + row_size]) >> 2;
06692   *p = (unsigned char)result;
06693   ++p;
06694   ++q;
06695 }
06696 
06697 ////////////////////////////////////////////////////////////////////
06698 //     Function: Texture::filter_2d_unsigned_short
06699 //       Access: Public, Static
06700 //  Description: Averages a 2x2 block of pixel components into a
06701 //               single pixel component, for producing the next mipmap
06702 //               level.  Increments p and q to the next component.
06703 ////////////////////////////////////////////////////////////////////
06704 void Texture::
06705 filter_2d_unsigned_short(unsigned char *&p, const unsigned char *&q,
06706                          size_t pixel_size, size_t row_size) {
06707   unsigned int result = ((unsigned int)*(unsigned short *)&q[0] +
06708                          (unsigned int)*(unsigned short *)&q[pixel_size] +
06709                          (unsigned int)*(unsigned short *)&q[row_size] +
06710                          (unsigned int)*(unsigned short *)&q[pixel_size + row_size]) >> 2;
06711   store_unscaled_short(p, result);
06712   q += 2;
06713 }
06714 
06715 ////////////////////////////////////////////////////////////////////
06716 //     Function: Texture::filter_3d_unsigned_byte
06717 //       Access: Public, Static
06718 //  Description: Averages a 2x2x2 block of pixel components into a
06719 //               single pixel component, for producing the next mipmap
06720 //               level.  Increments p and q to the next component.
06721 ////////////////////////////////////////////////////////////////////
06722 void Texture::
06723 filter_3d_unsigned_byte(unsigned char *&p, const unsigned char *&q,
06724                         size_t pixel_size, size_t row_size, size_t page_size) {
06725   unsigned int result = ((unsigned int)q[0] +
06726                          (unsigned int)q[pixel_size] +
06727                          (unsigned int)q[row_size] +
06728                          (unsigned int)q[pixel_size + row_size] +
06729                          (unsigned int)q[page_size] +
06730                          (unsigned int)q[pixel_size + page_size] +
06731                          (unsigned int)q[row_size + page_size] +
06732                          (unsigned int)q[pixel_size + row_size + page_size]) >> 3;
06733   *p = (unsigned char)result;
06734   ++p;
06735   ++q;
06736 }
06737 
06738 ////////////////////////////////////////////////////////////////////
06739 //     Function: Texture::filter_3d_unsigned_short
06740 //       Access: Public, Static
06741 //  Description: Averages a 2x2x2 block of pixel components into a
06742 //               single pixel component, for producing the next mipmap
06743 //               level.  Increments p and q to the next component.
06744 ////////////////////////////////////////////////////////////////////
06745 void Texture::
06746 filter_3d_unsigned_short(unsigned char *&p, const unsigned char *&q,
06747                          size_t pixel_size, size_t row_size,
06748                          size_t page_size) {
06749   unsigned int result = ((unsigned int)*(unsigned short *)&q[0] +
06750                          (unsigned int)*(unsigned short *)&q[pixel_size] +
06751                          (unsigned int)*(unsigned short *)&q[row_size] +
06752                          (unsigned int)*(unsigned short *)&q[pixel_size + row_size] +
06753                          (unsigned int)*(unsigned short *)&q[page_size] +
06754                          (unsigned int)*(unsigned short *)&q[pixel_size + page_size] +
06755                          (unsigned int)*(unsigned short *)&q[row_size + page_size] +
06756                          (unsigned int)*(unsigned short *)&q[pixel_size + row_size + page_size]) >> 3;
06757   store_unscaled_short(p, result);
06758   q += 2;
06759 }
06760 
06761 ////////////////////////////////////////////////////////////////////
06762 //     Function: Texture::do_squish
06763 //       Access: Private
06764 //  Description: Invokes the squish library to compress the RAM
06765 //               image(s).
06766 ////////////////////////////////////////////////////////////////////
06767 bool Texture::
06768 do_squish(CData *cdata, Texture::CompressionMode compression, int squish_flags) {
06769 #ifdef HAVE_SQUISH
06770   if (cdata->_ram_images.empty() || cdata->_ram_image_compression != CM_off) {
06771     return false;
06772   }
06773 
06774   if (!do_has_all_ram_mipmap_images(cdata)) {
06775     // If we're about to compress the RAM image, we should ensure that
06776     // we have all of the mipmap levels first.
06777     do_generate_ram_mipmap_images(cdata);
06778   }
06779 
06780   RamImages compressed_ram_images;
06781   compressed_ram_images.reserve(cdata->_ram_images.size());
06782   for (size_t n = 0; n < cdata->_ram_images.size(); ++n) {
06783     RamImage compressed_image;
06784     int x_size = do_get_expected_mipmap_x_size(cdata, n);
06785     int y_size = do_get_expected_mipmap_y_size(cdata, n);
06786     int num_pages = do_get_expected_mipmap_num_pages(cdata, n);
06787     int page_size = squish::GetStorageRequirements(x_size, y_size, squish_flags);
06788     int cell_size = squish::GetStorageRequirements(4, 4, squish_flags);
06789 
06790     compressed_image._page_size = page_size;
06791     compressed_image._image = PTA_uchar::empty_array(page_size * num_pages);
06792     for (int z = 0; z < num_pages; ++z) {
06793       unsigned char *dest_page = compressed_image._image.p() + z * page_size;
06794       unsigned const char *source_page = cdata->_ram_images[n]._image.p() + z * cdata->_ram_images[n]._page_size;
06795       unsigned const char *source_page_end = source_page + cdata->_ram_images[n]._page_size;
06796       // Convert one 4 x 4 cell at a time.
06797       unsigned char *d = dest_page;
06798       for (int y = 0; y < y_size; y += 4) {
06799         for (int x = 0; x < x_size; x += 4) {
06800           unsigned char tb[16 * 4];
06801           int mask = 0;
06802           unsigned char *t = tb;
06803           for (int i = 0; i < 16; ++i) {
06804             int xi = x + i % 4;
06805             int yi = y + i / 4;
06806             unsigned const char *s = source_page + (yi * x_size + xi) * cdata->_num_components;
06807             if (s < source_page_end) {
06808               switch (cdata->_num_components) {
06809               case 1:
06810                 t[0] = s[0];   // r
06811                 t[1] = s[0];   // g
06812                 t[2] = s[0];   // b
06813                 t[3] = 255;    // a
06814                 break;
06815 
06816               case 2:
06817                 t[0] = s[0];   // r
06818                 t[1] = s[0];   // g
06819                 t[2] = s[0];   // b
06820                 t[3] = s[1];   // a
06821                 break;
06822 
06823               case 3:
06824                 t[0] = s[2];   // r
06825                 t[1] = s[1];   // g
06826                 t[2] = s[0];   // b
06827                 t[3] = 255;    // a
06828                 break;
06829 
06830               case 4:
06831                 t[0] = s[2];   // r
06832                 t[1] = s[1];   // g
06833                 t[2] = s[0];   // b
06834                 t[3] = s[3];   // a
06835                 break;
06836               }
06837               mask |= (1 << i);
06838             }
06839             t += 4;
06840           }
06841           squish::CompressMasked(tb, mask, d, squish_flags);
06842           d += cell_size;
06843           Thread::consider_yield();
06844         }
06845       }
06846     }
06847     compressed_ram_images.push_back(compressed_image);
06848   }
06849   cdata->_ram_images.swap(compressed_ram_images);
06850   cdata->_ram_image_compression = compression;
06851   return true;
06852 
06853 #else  // HAVE_SQUISH
06854   return false;
06855 
06856 #endif  // HAVE_SQUISH
06857 }
06858 
06859 ////////////////////////////////////////////////////////////////////
06860 //     Function: Texture::do_unsquish
06861 //       Access: Private
06862 //  Description: Invokes the squish library to uncompress the RAM
06863 //               image(s).
06864 ////////////////////////////////////////////////////////////////////
06865 bool Texture::
06866 do_unsquish(CData *cdata, int squish_flags) {
06867 #ifdef HAVE_SQUISH
06868   if (cdata->_ram_images.empty()) {
06869     return false;
06870   }
06871   RamImages uncompressed_ram_images;
06872   uncompressed_ram_images.reserve(cdata->_ram_images.size());
06873   for (size_t n = 0; n < cdata->_ram_images.size(); ++n) {
06874     RamImage uncompressed_image;
06875     int x_size = do_get_expected_mipmap_x_size(cdata, n);
06876     int y_size = do_get_expected_mipmap_y_size(cdata, n);
06877     int num_pages = do_get_expected_mipmap_num_pages(cdata, n);
06878     int page_size = squish::GetStorageRequirements(x_size, y_size, squish_flags);
06879     int cell_size = squish::GetStorageRequirements(4, 4, squish_flags);
06880 
06881     uncompressed_image._page_size = do_get_expected_ram_mipmap_page_size(cdata, n);
06882     uncompressed_image._image = PTA_uchar::empty_array(uncompressed_image._page_size * num_pages);
06883     for (int z = 0; z < num_pages; ++z) {
06884       unsigned char *dest_page = uncompressed_image._image.p() + z * uncompressed_image._page_size;
06885       unsigned char *dest_page_end = dest_page + uncompressed_image._page_size;
06886       unsigned const char *source_page = cdata->_ram_images[n]._image.p() + z * page_size;
06887       // Unconvert one 4 x 4 cell at a time.
06888       unsigned const char *s = source_page;
06889       for (int y = 0; y < y_size; y += 4) {
06890         for (int x = 0; x < x_size; x += 4) {
06891           unsigned char tb[16 * 4];
06892           squish::Decompress(tb, s, squish_flags);
06893           s += cell_size;
06894 
06895           unsigned char *t = tb;
06896           for (int i = 0; i < 16; ++i) {
06897             int xi = x + i % 4;
06898             int yi = y + i / 4;
06899             unsigned char *d = dest_page + (yi * x_size + xi) * cdata->_num_components;
06900             if (d < dest_page_end) {
06901               switch (cdata->_num_components) {
06902               case 1:
06903                 d[0] = t[1];   // g
06904                 break;
06905 
06906               case 2:
06907                 d[0] = t[1];   // g
06908                 d[1] = t[3];   // a
06909                 break;
06910 
06911               case 3:
06912                 d[2] = t[0];   // r
06913                 d[1] = t[1];   // g
06914                 d[0] = t[2];   // b
06915                 break;
06916 
06917               case 4:
06918                 d[2] = t[0];   // r
06919                 d[1] = t[1];   // g
06920                 d[0] = t[2];   // b
06921                 d[3] = t[3];   // a
06922                 break;
06923               }
06924             }
06925             t += 4;
06926           }
06927         }
06928         Thread::consider_yield();
06929       }
06930     }
06931     uncompressed_ram_images.push_back(uncompressed_image);
06932   }
06933   cdata->_ram_images.swap(uncompressed_ram_images);
06934   cdata->_ram_image_compression = CM_off;
06935   return true;
06936 
06937 #else  // HAVE_SQUISH
06938   return false;
06939 
06940 #endif  // HAVE_SQUISH
06941 }
06942 
06943 ////////////////////////////////////////////////////////////////////
06944 //     Function: Texture::register_with_read_factory
06945 //       Access: Public, Static
06946 //  Description: Factory method to generate a Texture object
06947 ////////////////////////////////////////////////////////////////////
06948 void Texture::
06949 register_with_read_factory() {
06950   BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
06951 }
06952 
06953 ////////////////////////////////////////////////////////////////////
06954 //     Function: Texture::write_datagram
06955 //       Access: Public, Virtual
06956 //  Description: Function to write the important information in
06957 //               the particular object to a Datagram
06958 ////////////////////////////////////////////////////////////////////
06959 void Texture::
06960 write_datagram(BamWriter *manager, Datagram &me) {
06961   CDWriter cdata(_cycler, false);
06962 
06963   bool has_rawdata = false;
06964   do_write_datagram_header(cdata, manager, me, has_rawdata);
06965   do_write_datagram_body(cdata, manager, me);
06966 
06967   // If we are also including the texture's image data, then stuff it
06968   // in here.
06969   if (has_rawdata) {
06970     do_write_datagram_rawdata(cdata, manager, me);
06971   }
06972 }
06973 
06974 ////////////////////////////////////////////////////////////////////
06975 //     Function: Texture::finalize
06976 //       Access: Public, Virtual
06977 //  Description: Called by the BamReader to perform any final actions
06978 //               needed for setting up the object after all objects
06979 //               have been read and all pointers have been completed.
06980 ////////////////////////////////////////////////////////////////////
06981 void Texture::
06982 finalize(BamReader *) {
06983   // Unref the pointer that we explicitly reffed in make_from_bam().
06984   unref();
06985 
06986   // We should never get back to zero after unreffing our own count,
06987   // because we expect to have been stored in a pointer somewhere.  If
06988   // we do get to zero, it's a memory leak; the way to avoid this is
06989   // to call unref_delete() above instead of unref(), but this is
06990   // dangerous to do from within a virtual function.
06991   nassertv(get_ref_count() != 0);
06992 }
06993 
06994 
06995 ////////////////////////////////////////////////////////////////////
06996 //     Function: Texture::do_write_datagram_header
06997 //       Access: Protected
06998 //  Description: Writes the header part of the texture to the
06999 //               Datagram.  This is the common part that is shared by
07000 //               all Texture subclasses, and contains the filename and
07001 //               rawdata flags.  This method is not virtual because
07002 //               all Texture subclasses must write the same data at
07003 //               this step.
07004 //
07005 //               This part must be read first before calling
07006 //               do_fillin_body() to determine whether to load the
07007 //               Texture from the TexturePool or directly from the bam
07008 //               stream.
07009 //
07010 //               After this call, has_rawdata will be filled with
07011 //               either true or false, according to whether we expect
07012 //               to write the texture rawdata to the bam stream
07013 //               following the texture body.
07014 ////////////////////////////////////////////////////////////////////
07015 void Texture::
07016 do_write_datagram_header(CData *cdata, BamWriter *manager, Datagram &me, bool &has_rawdata) {
07017   // Write out the texture's raw pixel data if (a) the current Bam
07018   // Texture Mode requires that, or (b) there's no filename, so the
07019   // file can't be loaded up from disk, but the raw pixel data is
07020   // currently available in RAM.
07021 
07022   // Otherwise, we just write out the filename, and assume whoever
07023   // loads the bam file later will have access to the image file on
07024   // disk.
07025   BamWriter::BamTextureMode file_texture_mode = manager->get_file_texture_mode();
07026   has_rawdata = (file_texture_mode == BamWriter::BTM_rawdata || 
07027                  (cdata->_filename.empty() && do_has_bam_rawdata(cdata)));
07028   if (has_rawdata && !do_has_bam_rawdata(cdata)) {
07029     do_get_bam_rawdata(cdata);
07030     if (!do_has_bam_rawdata(cdata)) {
07031       // No image data after all.
07032       has_rawdata = false;
07033     }
07034   }
07035 
07036   bool has_bam_dir = !manager->get_filename().empty();
07037   Filename bam_dir = manager->get_filename().get_dirname();
07038   Filename filename = cdata->_filename;
07039   Filename alpha_filename = cdata->_alpha_filename;
07040 
07041   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
07042 
07043   switch (file_texture_mode) {
07044   case BamWriter::BTM_unchanged:
07045   case BamWriter::BTM_rawdata:
07046     break;
07047 
07048   case BamWriter::BTM_fullpath:
07049     filename = cdata->_fullpath;
07050     alpha_filename = cdata->_alpha_fullpath;
07051     break;
07052 
07053   case BamWriter::BTM_relative:
07054     filename = cdata->_fullpath;
07055     alpha_filename = cdata->_alpha_fullpath;
07056     bam_dir.make_absolute(vfs->get_cwd());
07057     if (!has_bam_dir || !filename.make_relative_to(bam_dir, true)) {
07058       filename.find_on_searchpath(get_model_path());
07059     }
07060     if (gobj_cat.is_debug()) {
07061       gobj_cat.debug()
07062         << "Texture file " << cdata->_fullpath
07063         << " found as " << filename << "\n";
07064     }
07065     if (!has_bam_dir || !alpha_filename.make_relative_to(bam_dir, true)) {
07066       alpha_filename.find_on_searchpath(get_model_path());
07067     }
07068     if (gobj_cat.is_debug()) {
07069       gobj_cat.debug()
07070         << "Alpha image " << cdata->_alpha_fullpath
07071         << " found as " << alpha_filename << "\n";
07072     }
07073     break;
07074 
07075   case BamWriter::BTM_basename:
07076     filename = cdata->_fullpath.get_basename();
07077     alpha_filename = cdata->_alpha_fullpath.get_basename();
07078     break;
07079 
07080   default:
07081     gobj_cat.error()
07082       << "Unsupported bam-texture-mode: " << (int)file_texture_mode << "\n";
07083   }
07084 
07085   if (filename.empty() && do_has_bam_rawdata(cdata)) {
07086     // If we don't have a filename, we have to store rawdata anyway.
07087     has_rawdata = true;
07088   }
07089 
07090   me.add_string(get_name());
07091   me.add_string(filename);
07092   me.add_string(alpha_filename);
07093   me.add_uint8(cdata->_primary_file_num_channels);
07094   me.add_uint8(cdata->_alpha_file_channel);
07095   me.add_bool(has_rawdata);
07096   me.add_uint8(cdata->_texture_type);
07097 }
07098 
07099 ////////////////////////////////////////////////////////////////////
07100 //     Function: Texture::do_write_datagram_body
07101 //       Access: Protected, Virtual
07102 //  Description: Writes the body part of the texture to the
07103 //               Datagram.  This is generally all of the texture
07104 //               parameters except for the header and the rawdata.
07105 ////////////////////////////////////////////////////////////////////
07106 void Texture::
07107 do_write_datagram_body(CData *cdata, BamWriter *manager, Datagram &me) {
07108   me.add_uint8(cdata->_wrap_u);
07109   me.add_uint8(cdata->_wrap_v);
07110   me.add_uint8(cdata->_wrap_w);
07111   me.add_uint8(cdata->_minfilter);
07112   me.add_uint8(cdata->_magfilter);
07113   me.add_int16(cdata->_anisotropic_degree);
07114   cdata->_border_color.write_datagram(me);
07115   me.add_uint8(cdata->_compression);
07116   me.add_uint8(cdata->_quality_level);
07117 
07118   me.add_uint8(cdata->_format);
07119   me.add_uint8(cdata->_num_components);
07120 
07121   me.add_uint8(cdata->_auto_texture_scale);
07122   me.add_uint32(cdata->_orig_file_x_size);
07123   me.add_uint32(cdata->_orig_file_y_size);
07124 
07125   bool has_simple_ram_image = !cdata->_simple_ram_image._image.empty();
07126   me.add_bool(has_simple_ram_image);
07127 
07128   // Write out the simple image too, so it will be available later.
07129   if (has_simple_ram_image) {
07130     me.add_uint32(cdata->_simple_x_size);
07131     me.add_uint32(cdata->_simple_y_size);
07132     me.add_int32(cdata->_simple_image_date_generated);
07133     me.add_uint32(cdata->_simple_ram_image._image.size());
07134     me.append_data(cdata->_simple_ram_image._image, cdata->_simple_ram_image._image.size());
07135   }
07136 }
07137 
07138 ////////////////////////////////////////////////////////////////////
07139 //     Function: Texture::do_write_datagram_rawdata
07140 //       Access: Protected, Virtual
07141 //  Description: Writes the rawdata part of the texture to the
07142 //               Datagram.
07143 ////////////////////////////////////////////////////////////////////
07144 void Texture::
07145 do_write_datagram_rawdata(CData *cdata, BamWriter *manager, Datagram &me) {
07146   me.add_uint32(cdata->_x_size);
07147   me.add_uint32(cdata->_y_size);
07148   me.add_uint32(cdata->_z_size);
07149 
07150   me.add_uint32(cdata->_pad_x_size);
07151   me.add_uint32(cdata->_pad_y_size);
07152   me.add_uint32(cdata->_pad_z_size);
07153 
07154   me.add_uint32(cdata->_num_views);
07155   me.add_uint8(cdata->_component_type);
07156   me.add_uint8(cdata->_component_width);
07157   me.add_uint8(cdata->_ram_image_compression);
07158   me.add_uint8(cdata->_ram_images.size());
07159   for (size_t n = 0; n < cdata->_ram_images.size(); ++n) {
07160     me.add_uint32(cdata->_ram_images[n]._page_size);
07161     me.add_uint32(cdata->_ram_images[n]._image.size());
07162     me.append_data(cdata->_ram_images[n]._image, cdata->_ram_images[n]._image.size());
07163   }
07164 }
07165 
07166 ////////////////////////////////////////////////////////////////////
07167 //     Function: Texture::make_from_bam
07168 //       Access: Protected, Static
07169 //  Description: Factory method to generate a Texture object
07170 ////////////////////////////////////////////////////////////////////
07171 TypedWritable *Texture::
07172 make_from_bam(const FactoryParams &params) {
07173   PT(Texture) dummy = new Texture;
07174   return dummy->make_this_from_bam(params);
07175 }
07176 
07177 ////////////////////////////////////////////////////////////////////
07178 //     Function: Texture::make_this_from_bam
07179 //       Access: Protected, Virtual
07180 //  Description: Called by make_from_bam() once the particular
07181 //               subclass of Texture is known.  This is called on a
07182 //               newly-constructed Texture object of the appropriate
07183 //               subclass.  It will return either the same Texture
07184 //               object (e.g. this), or a different Texture object
07185 //               loaded via the TexturePool, as appropriate.
07186 ////////////////////////////////////////////////////////////////////
07187 TypedWritable *Texture::
07188 make_this_from_bam(const FactoryParams &params) {
07189   // The process of making a texture is slightly different than making
07190   // other TypedWritable objects.  That is because all creation of
07191   // Textures should be done through calls to TexturePool, which
07192   // ensures that any loads of the same filename refer to the same
07193   // memory.
07194 
07195   DatagramIterator scan;
07196   BamReader *manager;
07197 
07198   parse_params(params, scan, manager);
07199 
07200   // Get the header information--the filenames and texture type--so we
07201   // can look up the file on disk first.
07202   string name = scan.get_string();
07203   Filename filename = scan.get_string();
07204   Filename alpha_filename = scan.get_string();
07205 
07206   int primary_file_num_channels = scan.get_uint8();
07207   int alpha_file_channel = scan.get_uint8();
07208   bool has_rawdata = scan.get_bool();
07209   TextureType texture_type = (TextureType)scan.get_uint8();
07210   if (manager->get_file_minor_ver() < 25) {
07211     // Between Panda3D releases 1.7.2 and 1.8.0 (bam versions 6.24 and
07212     // 6.25), we added TT_2d_texture_array, shifting the definition
07213     // for TT_cube_map.
07214     if (texture_type == TT_2d_texture_array) {
07215       texture_type = TT_cube_map;
07216     }
07217   }
07218 
07219   Texture *me = NULL;
07220   if (has_rawdata) {
07221     // If the raw image data is included, then just load the texture
07222     // directly from the stream, and return it.  In this case we
07223     // return the "this" pointer, since it's a newly-created Texture
07224     // object of the appropriate type.
07225     me = this;
07226     me->set_name(name);
07227     CDWriter cdata_me(me->_cycler, true);
07228     cdata_me->_filename = filename;
07229     cdata_me->_alpha_filename = alpha_filename;
07230     cdata_me->_primary_file_num_channels = primary_file_num_channels;
07231     cdata_me->_alpha_file_channel = alpha_file_channel;
07232     cdata_me->_texture_type = texture_type;
07233 
07234     // Read the texture attributes directly from the bam stream.
07235     me->do_fillin_body(cdata_me, scan, manager);
07236     me->do_fillin_rawdata(cdata_me, scan, manager);
07237 
07238     // To manage the reference count, explicitly ref it now, then
07239     // unref it in the finalize callback.
07240     me->ref();
07241     manager->register_finalize(me);
07242 
07243   } else {
07244     // The raw image data isn't included, so we'll be loading the
07245     // Texture via the TexturePool.  In this case we use the "this"
07246     // pointer as a temporary object to read all of the attributes
07247     // from the bam stream.
07248     Texture *dummy = this;
07249     AutoTextureScale auto_texture_scale = ATS_unspecified;
07250     {
07251       CDWriter cdata_dummy(dummy->_cycler, true);
07252       dummy->do_fillin_body(cdata_dummy, scan, manager);
07253       auto_texture_scale = cdata_dummy->_auto_texture_scale;
07254     }
07255 
07256     if (filename.empty()) {
07257       // This texture has no filename; since we don't have an image to
07258       // load, we can't actually create the texture.
07259       gobj_cat.info()
07260         << "Cannot create texture '" << name << "' with no filename.\n";
07261 
07262     } else {
07263       // This texture does have a filename, so try to load it from disk.
07264       VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
07265       if (!manager->get_filename().empty()) {
07266         // If texture filename was given relative to the bam filename,
07267         // expand it now.
07268         Filename bam_dir = manager->get_filename().get_dirname();
07269         vfs->resolve_filename(filename, bam_dir);
07270         if (!alpha_filename.empty()) {
07271           vfs->resolve_filename(alpha_filename, bam_dir);
07272         }
07273       }
07274 
07275       LoaderOptions options = manager->get_loader_options();
07276       if (dummy->uses_mipmaps()) {
07277         options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_generate_mipmaps);
07278       }
07279       options.set_auto_texture_scale(auto_texture_scale);
07280 
07281       switch (texture_type) {
07282       case TT_1d_texture:
07283       case TT_2d_texture:
07284         if (alpha_filename.empty()) {
07285           me = TexturePool::load_texture(filename, primary_file_num_channels,
07286                                          false, options);
07287         } else {
07288           me = TexturePool::load_texture(filename, alpha_filename,
07289                                          primary_file_num_channels,
07290                                          alpha_file_channel,
07291                                          false, options);
07292         }
07293         break;
07294 
07295       case TT_3d_texture:
07296         me = TexturePool::load_3d_texture(filename, false, options);
07297         break;
07298 
07299       case TT_2d_texture_array:
07300         me = TexturePool::load_2d_texture_array(filename, false, options);
07301         break;
07302 
07303       case TT_cube_map:
07304         me = TexturePool::load_cube_map(filename, false, options);
07305         break;
07306       }
07307     }
07308 
07309     if (me != (Texture *)NULL) {
07310       me->set_name(name);
07311       CDWriter cdata_me(me->_cycler, true);
07312       me->do_fillin_from(cdata_me, dummy);
07313 
07314       // Since in this case me was loaded from the TexturePool,
07315       // there's no need to explicitly manage the reference count.
07316       // TexturePool will hold it safely.
07317     }
07318   }
07319 
07320   return me;
07321 }
07322 
07323 ////////////////////////////////////////////////////////////////////
07324 //     Function: Texture::do_fillin_body
07325 //       Access: Protected, Virtual
07326 //  Description: Reads in the part of the Texture that was written
07327 //               with do_write_datagram_body().
07328 ////////////////////////////////////////////////////////////////////
07329 void Texture::
07330 do_fillin_body(CData *cdata, DatagramIterator &scan, BamReader *manager) {
07331   cdata->_wrap_u = (WrapMode)scan.get_uint8();
07332   cdata->_wrap_v = (WrapMode)scan.get_uint8();
07333   cdata->_wrap_w = (WrapMode)scan.get_uint8();
07334   cdata->_minfilter = (FilterType)scan.get_uint8();
07335   cdata->_magfilter = (FilterType)scan.get_uint8();
07336   cdata->_anisotropic_degree = scan.get_int16();
07337   cdata->_border_color.read_datagram(scan);
07338 
07339   if (manager->get_file_minor_ver() >= 1) {
07340     cdata->_compression = (CompressionMode)scan.get_uint8();
07341   }
07342   if (manager->get_file_minor_ver() >= 16) {
07343     cdata->_quality_level = (QualityLevel)scan.get_uint8();
07344   }
07345 
07346   cdata->_format = (Format)scan.get_uint8();
07347   cdata->_num_components = scan.get_uint8();
07348 
07349   ++(cdata->_properties_modified);
07350 
07351   cdata->_auto_texture_scale = ATS_unspecified;
07352   if (manager->get_file_minor_ver() >= 28) {
07353     cdata->_auto_texture_scale = (AutoTextureScale)scan.get_uint8();
07354   }
07355 
07356   bool has_simple_ram_image = false;
07357   if (manager->get_file_minor_ver() >= 18) {
07358     cdata->_orig_file_x_size = scan.get_uint32();
07359     cdata->_orig_file_y_size = scan.get_uint32();
07360 
07361     has_simple_ram_image = scan.get_bool();
07362   }
07363 
07364   if (has_simple_ram_image) {
07365     cdata->_simple_x_size = scan.get_uint32();
07366     cdata->_simple_y_size = scan.get_uint32();
07367     cdata->_simple_image_date_generated = scan.get_int32();
07368 
07369     size_t u_size = scan.get_uint32();
07370     PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type());
07371     for (size_t u_idx = 0; u_idx < u_size; ++u_idx) {
07372       image[(int)u_idx] = scan.get_uint8();
07373     }
07374 
07375     cdata->_simple_ram_image._image = image;
07376     cdata->_simple_ram_image._page_size = u_size;
07377     ++(cdata->_simple_image_modified);
07378   }
07379 }
07380 
07381 ////////////////////////////////////////////////////////////////////
07382 //     Function: Texture::do_fillin_rawdata
07383 //       Access: Protected, Virtual
07384 //  Description: Reads in the part of the Texture that was written
07385 //               with do_write_datagram_rawdata().
07386 ////////////////////////////////////////////////////////////////////
07387 void Texture::
07388 do_fillin_rawdata(CData *cdata, DatagramIterator &scan, BamReader *manager) {
07389   cdata->_x_size = scan.get_uint32();
07390   cdata->_y_size = scan.get_uint32();
07391   cdata->_z_size = scan.get_uint32();
07392 
07393   if (manager->get_file_minor_ver() >= 30) {
07394     cdata->_pad_x_size = scan.get_uint32();
07395     cdata->_pad_y_size = scan.get_uint32();
07396     cdata->_pad_z_size = scan.get_uint32();
07397   } else {
07398     do_set_pad_size(cdata, 0, 0, 0);
07399   }
07400 
07401   cdata->_num_views = 1;
07402   if (manager->get_file_minor_ver() >= 26) {
07403     cdata->_num_views = scan.get_uint32();
07404   }
07405   cdata->_component_type = (ComponentType)scan.get_uint8();
07406   cdata->_component_width = scan.get_uint8();
07407   cdata->_ram_image_compression = CM_off;
07408   if (manager->get_file_minor_ver() >= 1) {
07409     cdata->_ram_image_compression = (CompressionMode)scan.get_uint8();
07410   }
07411   
07412   int num_ram_images = 1;
07413   if (manager->get_file_minor_ver() >= 3) {
07414     num_ram_images = scan.get_uint8();
07415   }
07416   
07417   cdata->_ram_images.clear();
07418   cdata->_ram_images.reserve(num_ram_images);
07419   for (int n = 0; n < num_ram_images; ++n) {
07420     cdata->_ram_images.push_back(RamImage());
07421     cdata->_ram_images[n]._page_size = get_expected_ram_page_size();
07422     if (manager->get_file_minor_ver() >= 1) {
07423       cdata->_ram_images[n]._page_size = scan.get_uint32();
07424     }
07425     
07426     size_t u_size = scan.get_uint32();
07427     
07428     // fill the cdata->_image buffer with image data
07429     PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type());
07430     for (size_t u_idx = 0; u_idx < u_size; ++u_idx) {
07431       image[(int)u_idx] = scan.get_uint8();
07432     }
07433     cdata->_ram_images[n]._image = image;
07434   }
07435   cdata->_loaded_from_image = true;
07436   ++(cdata->_image_modified);
07437 }
07438 
07439 ////////////////////////////////////////////////////////////////////
07440 //     Function: Texture::do_fillin_from
07441 //       Access: Protected, Virtual
07442 //  Description: Called in make_from_bam(), this method properly
07443 //               copies the attributes from the bam stream (as stored
07444 //               in dummy) into this texture, updating the modified
07445 //               flags appropriately.
07446 ////////////////////////////////////////////////////////////////////
07447 void Texture::
07448 do_fillin_from(CData *cdata, const Texture *dummy) {
07449   // Use the setters instead of setting these directly, so we can
07450   // correctly avoid incrementing cdata->_properties_modified if none of
07451   // these actually change.  (Otherwise, we'd have to reload the
07452   // texture to the GSG every time we loaded a new bam file that
07453   // reference the texture, since each bam file reference passes
07454   // through this function.)
07455 
07456   CDReader cdata_dummy(dummy->_cycler);
07457 
07458   do_set_wrap_u(cdata, cdata_dummy->_wrap_u);
07459   do_set_wrap_v(cdata, cdata_dummy->_wrap_v);
07460   do_set_wrap_w(cdata, cdata_dummy->_wrap_w);
07461   do_set_border_color(cdata, cdata_dummy->_border_color);
07462 
07463   if (cdata_dummy->_minfilter != FT_default) {
07464     do_set_minfilter(cdata, cdata_dummy->_minfilter);
07465   }
07466   if (cdata_dummy->_magfilter != FT_default) {
07467     do_set_magfilter(cdata, cdata_dummy->_magfilter);
07468   }
07469   if (cdata_dummy->_anisotropic_degree != 0) {
07470     do_set_anisotropic_degree(cdata, cdata_dummy->_anisotropic_degree);
07471   }
07472   if (cdata_dummy->_compression != CM_default) {
07473     do_set_compression(cdata, cdata_dummy->_compression);
07474   }
07475   if (cdata_dummy->_quality_level != QL_default) {
07476     do_set_quality_level(cdata, cdata_dummy->_quality_level);
07477   }
07478 
07479   Format format = cdata_dummy->_format;
07480   int num_components = cdata_dummy->_num_components;
07481 
07482   if (num_components == cdata->_num_components) {
07483     // Only reset the format if the number of components hasn't
07484     // changed, since if the number of components has changed our
07485     // texture no longer matches what it was when the bam was
07486     // written.
07487     do_set_format(cdata, format);
07488   }
07489 
07490   if (!cdata_dummy->_simple_ram_image._image.empty()) {
07491     // Only replace the simple ram image if it was generated more
07492     // recently than the one we already have.
07493     if (cdata->_simple_ram_image._image.empty() ||
07494         cdata_dummy->_simple_image_date_generated > cdata->_simple_image_date_generated) {
07495       do_set_simple_ram_image(cdata, 
07496                               cdata_dummy->_simple_ram_image._image,
07497                               cdata_dummy->_simple_x_size,
07498                               cdata_dummy->_simple_y_size);
07499       cdata->_simple_image_date_generated = cdata_dummy->_simple_image_date_generated;
07500     }
07501   }
07502 }
07503 
07504 ////////////////////////////////////////////////////////////////////
07505 //     Function: Texture::CData::Constructor
07506 //       Access: Public
07507 //  Description: 
07508 ////////////////////////////////////////////////////////////////////
07509 Texture::CData::
07510 CData() {
07511   _primary_file_num_channels = 0;
07512   _alpha_file_channel = 0;
07513   _magfilter = FT_default;
07514   _minfilter = FT_default;
07515   _wrap_u = WM_repeat;
07516   _wrap_v = WM_repeat;
07517   _wrap_w = WM_repeat;
07518   _anisotropic_degree = 0;
07519   _keep_ram_image = true;
07520   _border_color.set(0.0f, 0.0f, 0.0f, 1.0f);
07521   _compression = CM_default;
07522   _auto_texture_scale = ATS_unspecified;
07523   _ram_image_compression = CM_off;
07524   _render_to_texture = false;
07525   _match_framebuffer_format = false;
07526   _post_load_store_cache = false;
07527   _quality_level = QL_default;
07528 
07529   _texture_type = TT_2d_texture;
07530   _x_size = 0;
07531   _y_size = 1;
07532   _z_size = 1;
07533   _num_views = 1;
07534 
07535   // We will override the format in a moment (in the Texture
07536   // constructor), but set it to something else first to avoid the
07537   // check in do_set_format depending on an uninitialized value.
07538   _format = F_rgba;
07539 
07540   _pad_x_size = 0;
07541   _pad_y_size = 0;
07542   _pad_z_size = 0;
07543 
07544   _orig_file_x_size = 0;
07545   _orig_file_y_size = 0;
07546 
07547   _loaded_from_image = false;
07548   _loaded_from_txo = false;
07549   _has_read_pages = false;
07550   _has_read_mipmaps = false;
07551   _num_mipmap_levels_read = 0;
07552 
07553   _simple_x_size = 0;
07554   _simple_y_size = 0;
07555   _simple_ram_image._page_size = 0;
07556 }
07557 
07558 ////////////////////////////////////////////////////////////////////
07559 //     Function: Texture::CData::Copy Constructor
07560 //       Access: Public
07561 //  Description: 
07562 ////////////////////////////////////////////////////////////////////
07563 Texture::CData::
07564 CData(const Texture::CData &copy) {
07565   _num_mipmap_levels_read = 0;
07566 
07567   do_assign(&copy);
07568 
07569   _properties_modified = copy._properties_modified;
07570   _image_modified = copy._image_modified;
07571   _simple_image_modified = copy._simple_image_modified;
07572 }
07573 
07574 ////////////////////////////////////////////////////////////////////
07575 //     Function: Texture::CData::make_copy
07576 //       Access: Public, Virtual
07577 //  Description:
07578 ////////////////////////////////////////////////////////////////////
07579 CycleData *Texture::CData::
07580 make_copy() const {
07581   return new CData(*this);
07582 }
07583 
07584 ////////////////////////////////////////////////////////////////////
07585 //     Function: Texture::CData::do_assign
07586 //       Access: Public
07587 //  Description: 
07588 ////////////////////////////////////////////////////////////////////
07589 void Texture::CData::
07590 do_assign(const Texture::CData *copy) {
07591   _filename = copy->_filename;
07592   _alpha_filename = copy->_alpha_filename;
07593   if (!copy->_fullpath.empty()) {
07594     // Since the fullpath is often empty on a file loaded directly
07595     // from a txo, we only assign the fullpath if it is not empty.
07596     _fullpath = copy->_fullpath;
07597     _alpha_fullpath = copy->_alpha_fullpath;
07598   }
07599   _primary_file_num_channels = copy->_primary_file_num_channels;
07600   _alpha_file_channel = copy->_alpha_file_channel;
07601   _x_size = copy->_x_size;
07602   _y_size = copy->_y_size;
07603   _z_size = copy->_z_size;
07604   _num_views = copy->_num_views;
07605   _pad_x_size = copy->_pad_x_size;
07606   _pad_y_size = copy->_pad_y_size;
07607   _pad_z_size = copy->_pad_z_size;
07608   _orig_file_x_size = copy->_orig_file_x_size;
07609   _orig_file_y_size = copy->_orig_file_y_size;
07610   _num_components = copy->_num_components;
07611   _component_width = copy->_component_width;
07612   _texture_type = copy->_texture_type;
07613   _format = copy->_format;
07614   _component_type = copy->_component_type;
07615   _loaded_from_image = copy->_loaded_from_image;
07616   _loaded_from_txo = copy->_loaded_from_txo;
07617   _has_read_pages = copy->_has_read_pages;
07618   _has_read_mipmaps = copy->_has_read_mipmaps;
07619   _num_mipmap_levels_read = copy->_num_mipmap_levels_read;
07620   _wrap_u = copy->_wrap_u;
07621   _wrap_v = copy->_wrap_v;
07622   _wrap_w = copy->_wrap_w;
07623   _minfilter = copy->_minfilter;
07624   _magfilter = copy->_magfilter;
07625   _anisotropic_degree = copy->_anisotropic_degree;
07626   _keep_ram_image = copy->_keep_ram_image;
07627   _border_color = copy->_border_color;
07628   _compression = copy->_compression;
07629   _match_framebuffer_format = copy->_match_framebuffer_format;
07630   _quality_level = copy->_quality_level;
07631   _auto_texture_scale = copy->_auto_texture_scale;
07632   _ram_image_compression = copy->_ram_image_compression;
07633   _ram_images = copy->_ram_images;
07634   _simple_x_size = copy->_simple_x_size;
07635   _simple_y_size = copy->_simple_y_size;
07636   _simple_ram_image = copy->_simple_ram_image;
07637 }
07638 
07639 ////////////////////////////////////////////////////////////////////
07640 //     Function: Texture::CData::write_datagram
07641 //       Access: Public, Virtual
07642 //  Description: Writes the contents of this object to the datagram
07643 //               for shipping out to a Bam file.
07644 ////////////////////////////////////////////////////////////////////
07645 void Texture::CData::
07646 write_datagram(BamWriter *manager, Datagram &dg) const {
07647 }
07648 
07649 ////////////////////////////////////////////////////////////////////
07650 //     Function: Texture::CData::complete_pointers
07651 //       Access: Public, Virtual
07652 //  Description: Receives an array of pointers, one for each time
07653 //               manager->read_pointer() was called in fillin().
07654 //               Returns the number of pointers processed.
07655 ////////////////////////////////////////////////////////////////////
07656 int Texture::CData::
07657 complete_pointers(TypedWritable **p_list, BamReader *manager) {
07658   return 0;
07659 }
07660 
07661 ////////////////////////////////////////////////////////////////////
07662 //     Function: Texture::CData::fillin
07663 //       Access: Public, Virtual
07664 //  Description: This internal function is called by make_from_bam to
07665 //               read in all of the relevant data from the BamFile for
07666 //               the new Geom.
07667 ////////////////////////////////////////////////////////////////////
07668 void Texture::CData::
07669 fillin(DatagramIterator &scan, BamReader *manager) {
07670 }
07671 
07672 ////////////////////////////////////////////////////////////////////
07673 //     Function: Texture::TextureType output operator
07674 //  Description:
07675 ////////////////////////////////////////////////////////////////////
07676 ostream &
07677 operator << (ostream &out, Texture::TextureType tt) {
07678   return out << Texture::format_texture_type(tt);
07679 }
07680 
07681 ////////////////////////////////////////////////////////////////////
07682 //     Function: Texture::ComponentType output operator
07683 //  Description:
07684 ////////////////////////////////////////////////////////////////////
07685 ostream &
07686 operator << (ostream &out, Texture::ComponentType ct) {
07687   return out << Texture::format_component_type(ct);
07688 }
07689 
07690 ////////////////////////////////////////////////////////////////////
07691 //     Function: Texture::Format output operator
07692 //  Description:
07693 ////////////////////////////////////////////////////////////////////
07694 ostream &
07695 operator << (ostream &out, Texture::Format f) {
07696   return out << Texture::format_format(f);
07697 }
07698 
07699 ////////////////////////////////////////////////////////////////////
07700 //     Function: Texture::FilterType output operator
07701 //  Description:
07702 ////////////////////////////////////////////////////////////////////
07703 ostream &
07704 operator << (ostream &out, Texture::FilterType ft) {
07705   return out << Texture::format_filter_type(ft);
07706 }
07707 
07708 ////////////////////////////////////////////////////////////////////
07709 //     Function: Texture::FilterType input operator
07710 //  Description:
07711 ////////////////////////////////////////////////////////////////////
07712 istream &
07713 operator >> (istream &in, Texture::FilterType &ft) {
07714   string word;
07715   in >> word;
07716 
07717   ft = Texture::string_filter_type(word);
07718   return in;
07719 }
07720 
07721 ////////////////////////////////////////////////////////////////////
07722 //     Function: Texture::WrapMode output operator
07723 //  Description:
07724 ////////////////////////////////////////////////////////////////////
07725 ostream &
07726 operator << (ostream &out, Texture::WrapMode wm) {
07727   return out << Texture::format_wrap_mode(wm);
07728 }
07729 
07730 ////////////////////////////////////////////////////////////////////
07731 //     Function: Texture::WrapMode input operator
07732 //  Description:
07733 ////////////////////////////////////////////////////////////////////
07734 istream &
07735 operator >> (istream &in, Texture::WrapMode &wm) {
07736   string word;
07737   in >> word;
07738 
07739   wm = Texture::string_wrap_mode(word);
07740   return in;
07741 }
07742 
07743 ////////////////////////////////////////////////////////////////////
07744 //     Function: Texture::CompressionMode output operator
07745 //  Description:
07746 ////////////////////////////////////////////////////////////////////
07747 ostream &
07748 operator << (ostream &out, Texture::CompressionMode cm) {
07749   return out << Texture::format_compression_mode(cm);
07750 }
07751 
07752 ////////////////////////////////////////////////////////////////////
07753 //     Function: Texture::QualityLevel output operator
07754 //  Description:
07755 ////////////////////////////////////////////////////////////////////
07756 ostream &
07757 operator << (ostream &out, Texture::QualityLevel tql) {
07758   return out << Texture::format_quality_level(tql);
07759 }
07760 
07761 ////////////////////////////////////////////////////////////////////
07762 //     Function: Texture::QualityLevel input operator
07763 //  Description:
07764 ////////////////////////////////////////////////////////////////////
07765 istream &
07766 operator >> (istream &in, Texture::QualityLevel &tql) {
07767   string word;
07768   in >> word;
07769 
07770   tql = Texture::string_quality_level(word);
07771   return in;
07772 }
 All Classes Functions Variables Enumerations