Panda3D
|
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 ©) : 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 ©) { 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 ©) { 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 ¶ms) { 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 }