Panda3D

texture.cxx

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