Panda3D
texture.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file texture.cxx
10  * @author mike
11  * @date 1997-01-09
12  * @author fperazzi, PandaSE
13  * @date 2010-04-29
14  */
15 
16 #include "pandabase.h"
17 #include "texture.h"
18 #include "config_gobj.h"
19 #include "config_putil.h"
20 #include "texturePool.h"
21 #include "textureContext.h"
22 #include "bamCache.h"
23 #include "bamCacheRecord.h"
24 #include "datagram.h"
25 #include "datagramIterator.h"
26 #include "bamReader.h"
27 #include "bamWriter.h"
28 #include "string_utils.h"
30 #include "pnmImage.h"
31 #include "pnmReader.h"
32 #include "pfmFile.h"
33 #include "virtualFileSystem.h"
34 #include "datagramInputFile.h"
35 #include "datagramOutputFile.h"
36 #include "bam.h"
37 #include "zStream.h"
38 #include "indent.h"
39 #include "cmath.h"
40 #include "pStatTimer.h"
41 #include "pbitops.h"
42 #include "streamReader.h"
43 #include "texturePeeker.h"
44 #include "convert_srgb.h"
45 
46 #ifdef HAVE_SQUISH
47 #include <squish.h>
48 #endif // HAVE_SQUISH
49 
50 #include <stddef.h>
51 
52 using std::endl;
53 using std::istream;
54 using std::max;
55 using std::min;
56 using std::ostream;
57 using std::string;
58 using std::swap;
59 
61 ("texture-quality-level", Texture::QL_normal,
62  PRC_DESC("This specifies a global quality level for all textures. You "
63  "may specify either fastest, normal, or best. This actually "
64  "affects the meaning of Texture::set_quality_level(QL_default), "
65  "so it may be overridden on a per-texture basis. This generally "
66  "only has an effect when using the tinydisplay software renderer; "
67  "it has little or no effect on normal, hardware-accelerated "
68  "renderers. See Texture::set_quality_level()."));
69 
70 PStatCollector Texture::_texture_read_pcollector("*:Texture:Read");
71 TypeHandle Texture::_type_handle;
72 TypeHandle Texture::CData::_type_handle;
73 AutoTextureScale Texture::_textures_power_2 = ATS_unspecified;
74 
75 // Stuff to read and write DDS files.
76 
77 // little-endian, of course
78 #define DDS_MAGIC 0x20534444
79 
80 
81 // DDS_header.dwFlags
82 #define DDSD_CAPS 0x00000001
83 #define DDSD_HEIGHT 0x00000002
84 #define DDSD_WIDTH 0x00000004
85 #define DDSD_PITCH 0x00000008
86 #define DDSD_PIXELFORMAT 0x00001000
87 #define DDSD_MIPMAPCOUNT 0x00020000
88 #define DDSD_LINEARSIZE 0x00080000
89 #define DDSD_DEPTH 0x00800000
90 
91 // DDS_header.sPixelFormat.dwFlags
92 #define DDPF_ALPHAPIXELS 0x00000001
93 #define DDPF_FOURCC 0x00000004
94 #define DDPF_INDEXED 0x00000020
95 #define DDPF_RGB 0x00000040
96 
97 // DDS_header.sCaps.dwCaps1
98 #define DDSCAPS_COMPLEX 0x00000008
99 #define DDSCAPS_TEXTURE 0x00001000
100 #define DDSCAPS_MIPMAP 0x00400000
101 
102 // DDS_header.sCaps.dwCaps2
103 #define DDSCAPS2_CUBEMAP 0x00000200
104 #define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400
105 #define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800
106 #define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000
107 #define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000
108 #define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000
109 #define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000
110 #define DDSCAPS2_VOLUME 0x00200000
111 
112 struct DDSPixelFormat {
113  unsigned int pf_size;
114  unsigned int pf_flags;
115  unsigned int four_cc;
116  unsigned int rgb_bitcount;
117  unsigned int r_mask;
118  unsigned int g_mask;
119  unsigned int b_mask;
120  unsigned int a_mask;
121 };
122 
123 struct DDSCaps2 {
124  unsigned int caps1;
125  unsigned int caps2;
126  unsigned int ddsx;
127 };
128 
129 struct DDSHeader {
130  unsigned int dds_magic;
131  unsigned int dds_size;
132  unsigned int dds_flags;
133  unsigned int height;
134  unsigned int width;
135  unsigned int pitch;
136  unsigned int depth;
137  unsigned int num_levels;
138 
139  DDSPixelFormat pf;
140  DDSCaps2 caps;
141 };
142 
143 // Stuff to read KTX files.
144 enum KTXType {
145  KTX_BYTE = 0x1400,
146  KTX_UNSIGNED_BYTE = 0x1401,
147  KTX_SHORT = 0x1402,
148  KTX_UNSIGNED_SHORT = 0x1403,
149  KTX_INT = 0x1404,
150  KTX_UNSIGNED_INT = 0x1405,
151  KTX_FLOAT = 0x1406,
152  KTX_HALF_FLOAT = 0x140B,
153  KTX_UNSIGNED_BYTE_3_3_2 = 0x8032,
154  KTX_UNSIGNED_SHORT_4_4_4_4 = 0x8033,
155  KTX_UNSIGNED_SHORT_5_5_5_1 = 0x8034,
156  KTX_UNSIGNED_INT_8_8_8_8 = 0x8035,
157  KTX_UNSIGNED_INT_10_10_10_2 = 0x8036,
158  KTX_UNSIGNED_BYTE_2_3_3_REV = 0x8362,
159  KTX_UNSIGNED_SHORT_5_6_5 = 0x8363,
160  KTX_UNSIGNED_SHORT_5_6_5_REV = 0x8364,
161  KTX_UNSIGNED_SHORT_4_4_4_4_REV = 0x8365,
162  KTX_UNSIGNED_SHORT_1_5_5_5_REV = 0x8366,
163  KTX_UNSIGNED_INT_8_8_8_8_REV = 0x8367,
164  KTX_UNSIGNED_INT_2_10_10_10_REV = 0x8368,
165  KTX_UNSIGNED_INT_24_8 = 0x84FA,
166  KTX_UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B,
167  KTX_UNSIGNED_INT_5_9_9_9_REV = 0x8C3E,
168  KTX_FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD,
169 };
170 
171 enum KTXFormat {
172  KTX_ALPHA = 0x1906,
173  KTX_ALPHA12 = 0x803D,
174  KTX_ALPHA16 = 0x803E,
175  KTX_ALPHA16_SNORM = 0x9018,
176  KTX_ALPHA4 = 0x803B,
177  KTX_ALPHA8 = 0x803C,
178  KTX_ALPHA8_SNORM = 0x9014,
179  KTX_ALPHA_SNORM = 0x9010,
180  KTX_BGR = 0x80E0,
181  KTX_BGR_INTEGER = 0x8D9A,
182  KTX_BGRA = 0x80E1,
183  KTX_BGRA_INTEGER = 0x8D9B,
184  KTX_BLUE = 0x1905,
185  KTX_BLUE_INTEGER = 0x8D96,
186  KTX_COLOR_INDEX = 0x1900,
187  KTX_DEPTH24_STENCIL8 = 0x88F0,
188  KTX_DEPTH32F_STENCIL8 = 0x8CAD,
189  KTX_DEPTH_COMPONENT = 0x1902,
190  KTX_DEPTH_COMPONENT16 = 0x81A5,
191  KTX_DEPTH_COMPONENT24 = 0x81A6,
192  KTX_DEPTH_COMPONENT32 = 0x81A7,
193  KTX_DEPTH_COMPONENT32F = 0x8CAC,
194  KTX_DEPTH_STENCIL = 0x84F9,
195  KTX_GREEN = 0x1904,
196  KTX_GREEN_INTEGER = 0x8D95,
197  KTX_INTENSITY = 0x8049,
198  KTX_INTENSITY12 = 0x804C,
199  KTX_INTENSITY16 = 0x804D,
200  KTX_INTENSITY16_SNORM = 0x901B,
201  KTX_INTENSITY4 = 0x804A,
202  KTX_INTENSITY8 = 0x804B,
203  KTX_INTENSITY8_SNORM = 0x9017,
204  KTX_INTENSITY_SNORM = 0x9013,
205  KTX_LUMINANCE = 0x1909,
206  KTX_LUMINANCE12 = 0x8041,
207  KTX_LUMINANCE12_ALPHA12 = 0x8047,
208  KTX_LUMINANCE12_ALPHA4 = 0x8046,
209  KTX_LUMINANCE16 = 0x8042,
210  KTX_LUMINANCE16_ALPHA16 = 0x8048,
211  KTX_LUMINANCE16_ALPHA16_SNORM = 0x901A,
212  KTX_LUMINANCE16_SNORM = 0x9019,
213  KTX_LUMINANCE4 = 0x803F,
214  KTX_LUMINANCE4_ALPHA4 = 0x8043,
215  KTX_LUMINANCE6_ALPHA2 = 0x8044,
216  KTX_LUMINANCE8 = 0x8040,
217  KTX_LUMINANCE8_ALPHA8 = 0x8045,
218  KTX_LUMINANCE8_ALPHA8_SNORM = 0x9016,
219  KTX_LUMINANCE8_SNORM = 0x9015,
220  KTX_LUMINANCE_ALPHA = 0x190A,
221  KTX_LUMINANCE_ALPHA_SNORM = 0x9012,
222  KTX_LUMINANCE_SNORM = 0x9011,
223  KTX_R11F_G11F_B10F = 0x8C3A,
224  KTX_R16 = 0x822A,
225  KTX_R16_SNORM = 0x8F98,
226  KTX_R16F = 0x822D,
227  KTX_R16I = 0x8233,
228  KTX_R16UI = 0x8234,
229  KTX_R32F = 0x822E,
230  KTX_R32I = 0x8235,
231  KTX_R32UI = 0x8236,
232  KTX_R3_G3_B2 = 0x2A10,
233  KTX_R8 = 0x8229,
234  KTX_R8_SNORM = 0x8F94,
235  KTX_R8I = 0x8231,
236  KTX_R8UI = 0x8232,
237  KTX_RED = 0x1903,
238  KTX_RED_INTEGER = 0x8D94,
239  KTX_RED_SNORM = 0x8F90,
240  KTX_RG = 0x8227,
241  KTX_RG16 = 0x822C,
242  KTX_RG16_SNORM = 0x8F99,
243  KTX_RG16F = 0x822F,
244  KTX_RG16I = 0x8239,
245  KTX_RG16UI = 0x823A,
246  KTX_RG32F = 0x8230,
247  KTX_RG32I = 0x823B,
248  KTX_RG32UI = 0x823C,
249  KTX_RG8 = 0x822B,
250  KTX_RG8_SNORM = 0x8F95,
251  KTX_RG8I = 0x8237,
252  KTX_RG8UI = 0x8238,
253  KTX_RG_INTEGER = 0x8228,
254  KTX_RG_SNORM = 0x8F91,
255  KTX_RGB = 0x1907,
256  KTX_RGB10 = 0x8052,
257  KTX_RGB10_A2 = 0x8059,
258  KTX_RGB12 = 0x8053,
259  KTX_RGB16 = 0x8054,
260  KTX_RGB16_SNORM = 0x8F9A,
261  KTX_RGB16F = 0x881B,
262  KTX_RGB16I = 0x8D89,
263  KTX_RGB16UI = 0x8D77,
264  KTX_RGB2 = 0x804E,
265  KTX_RGB32F = 0x8815,
266  KTX_RGB32I = 0x8D83,
267  KTX_RGB32UI = 0x8D71,
268  KTX_RGB4 = 0x804F,
269  KTX_RGB5 = 0x8050,
270  KTX_RGB5_A1 = 0x8057,
271  KTX_RGB8 = 0x8051,
272  KTX_RGB8_SNORM = 0x8F96,
273  KTX_RGB8I = 0x8D8F,
274  KTX_RGB8UI = 0x8D7D,
275  KTX_RGB9_E5 = 0x8C3D,
276  KTX_RGB_INTEGER = 0x8D98,
277  KTX_RGB_SNORM = 0x8F92,
278  KTX_RGBA = 0x1908,
279  KTX_RGBA12 = 0x805A,
280  KTX_RGBA16 = 0x805B,
281  KTX_RGBA16_SNORM = 0x8F9B,
282  KTX_RGBA16F = 0x881A,
283  KTX_RGBA16I = 0x8D88,
284  KTX_RGBA16UI = 0x8D76,
285  KTX_RGBA2 = 0x8055,
286  KTX_RGBA32F = 0x8814,
287  KTX_RGBA32I = 0x8D82,
288  KTX_RGBA32UI = 0x8D70,
289  KTX_RGBA4 = 0x8056,
290  KTX_RGBA8 = 0x8058,
291  KTX_RGBA8_SNORM = 0x8F97,
292  KTX_RGBA8I = 0x8D8E,
293  KTX_RGBA8UI = 0x8D7C,
294  KTX_RGBA_INTEGER = 0x8D99,
295  KTX_RGBA_SNORM = 0x8F93,
296  KTX_SLUMINANCE = 0x8C46,
297  KTX_SLUMINANCE8 = 0x8C47,
298  KTX_SLUMINANCE8_ALPHA8 = 0x8C45,
299  KTX_SLUMINANCE_ALPHA = 0x8C44,
300  KTX_SRGB = 0x8C40,
301  KTX_SRGB8 = 0x8C41,
302  KTX_SRGB8_ALPHA8 = 0x8C43,
303  KTX_SRGB_ALPHA = 0x8C42,
304  KTX_STENCIL_INDEX = 0x1901,
305  KTX_STENCIL_INDEX1 = 0x8D46,
306  KTX_STENCIL_INDEX16 = 0x8D49,
307  KTX_STENCIL_INDEX4 = 0x8D47,
308  KTX_STENCIL_INDEX8 = 0x8D48,
309 };
310 
311 enum KTXCompressedFormat {
312  KTX_COMPRESSED_LUMINANCE_ALPHA_LATC2 = 0x8C72,
313  KTX_COMPRESSED_LUMINANCE_LATC1 = 0x8C70,
314  KTX_COMPRESSED_R11_EAC = 0x9270,
315  KTX_COMPRESSED_RED = 0x8225,
316  KTX_COMPRESSED_RED_RGTC1 = 0x8DBB,
317  KTX_COMPRESSED_RG = 0x8226,
318  KTX_COMPRESSED_RG11_EAC = 0x9272,
319  KTX_COMPRESSED_RG_RGTC2 = 0x8DBD,
320  KTX_COMPRESSED_RGB = 0x84ED,
321  KTX_COMPRESSED_RGB8_ETC2 = 0x9274,
322  KTX_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276,
323  KTX_COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E,
324  KTX_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F,
325  KTX_COMPRESSED_RGB_FXT1_3DFX = 0x86B0,
326  KTX_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01,
327  KTX_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00,
328  KTX_COMPRESSED_RGB_S3TC_DXT1 = 0x83F0,
329  KTX_COMPRESSED_RGBA = 0x84EE,
330  KTX_COMPRESSED_RGBA8_ETC2_EAC = 0x9278,
331  KTX_COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C,
332  KTX_COMPRESSED_RGBA_FXT1_3DFX = 0x86B1,
333  KTX_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03,
334  KTX_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG = 0x9137,
335  KTX_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02,
336  KTX_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG = 0x9138,
337  KTX_COMPRESSED_RGBA_S3TC_DXT1 = 0x83F1,
338  KTX_COMPRESSED_RGBA_S3TC_DXT3 = 0x83F2,
339  KTX_COMPRESSED_RGBA_S3TC_DXT5 = 0x83F3,
340  KTX_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2 = 0x8C73,
341  KTX_COMPRESSED_SIGNED_LUMINANCE_LATC1 = 0x8C71,
342  KTX_COMPRESSED_SIGNED_R11_EAC = 0x9271,
343  KTX_COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC,
344  KTX_COMPRESSED_SIGNED_RG11_EAC = 0x9273,
345  KTX_COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE,
346  KTX_COMPRESSED_SLUMINANCE = 0x8C4A,
347  KTX_COMPRESSED_SLUMINANCE_ALPHA = 0x8C4B,
348  KTX_COMPRESSED_SRGB = 0x8C48,
349  KTX_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279,
350  KTX_COMPRESSED_SRGB8_ETC2 = 0x9275,
351  KTX_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277,
352  KTX_COMPRESSED_SRGB_ALPHA = 0x8C49,
353  KTX_COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D,
354  KTX_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1 = 0x8A56,
355  KTX_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2 = 0x93F0,
356  KTX_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1 = 0x8A57,
357  KTX_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2 = 0x93F1,
358  KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT1 = 0x8C4D,
359  KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT3 = 0x8C4E,
360  KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT5 = 0x8C4F,
361  KTX_COMPRESSED_SRGB_PVRTC_2BPPV1 = 0x8A54,
362  KTX_COMPRESSED_SRGB_PVRTC_4BPPV1 = 0x8A55,
363  KTX_COMPRESSED_SRGB_S3TC_DXT1 = 0x8C4C,
364  KTX_ETC1_RGB8 = 0x8D64,
365  KTX_ETC1_SRGB8 = 0x88EE,
366 };
367 
368 /**
369  * Constructs an empty texture. The default is to set up the texture as an
370  * empty 2-d texture; follow up with one of the variants of setup_texture() if
371  * this is not what you want.
372  */
373 Texture::
374 Texture(const string &name) :
375  Namable(name),
376  _lock(name),
377  _cvar(_lock)
378 {
379  _reloading = false;
380 
381  CDWriter cdata(_cycler, true);
382  do_set_format(cdata, F_rgb);
383  do_set_component_type(cdata, T_unsigned_byte);
384 }
385 
386 /**
387  * Use Texture::make_copy() to make a duplicate copy of an existing Texture.
388  */
389 Texture::
390 Texture(const Texture &copy) :
391  Namable(copy),
392  _cycler(copy._cycler),
393  _lock(copy.get_name()),
394  _cvar(_lock)
395 {
396  _reloading = false;
397 }
398 
399 /**
400  * Use Texture::make_copy() to make a duplicate copy of an existing Texture.
401  */
402 void Texture::
403 operator = (const Texture &copy) {
404  Namable::operator = (copy);
405  _cycler = copy._cycler;
406 }
407 
408 /**
409  *
410  */
411 Texture::
412 ~Texture() {
413  release_all();
414  nassertv(!_reloading);
415 }
416 
417 /**
418  * Generates a special cube map image in the texture that can be used to apply
419  * bump mapping effects: for each texel in the cube map that is indexed by the
420  * 3-d texture coordinates (x, y, z), the resulting value is the normalized
421  * vector (x, y, z) (compressed from -1..1 into 0..1).
422  */
423 void Texture::
425  CDWriter cdata(_cycler, true);
426  do_setup_texture(cdata, TT_cube_map, size, size, 6, T_unsigned_byte, F_rgb);
427  PTA_uchar image = do_make_ram_image(cdata);
428  cdata->_keep_ram_image = true;
429 
430  cdata->inc_image_modified();
431  cdata->inc_properties_modified();
432 
433  PN_stdfloat half_size = (PN_stdfloat)size * 0.5f;
434  PN_stdfloat center = half_size - 0.5f;
435 
436  LMatrix4 scale
437  (127.5f, 0.0f, 0.0f, 0.0f,
438  0.0f, 127.5f, 0.0f, 0.0f,
439  0.0f, 0.0f, 127.5f, 0.0f,
440  127.5f, 127.5f, 127.5f, 1.0f);
441 
442  unsigned char *p = image;
443  int xi, yi;
444 
445  // Page 0: positive X.
446  for (yi = 0; yi < size; ++yi) {
447  for (xi = 0; xi < size; ++xi) {
448  LVector3 vec(half_size, center - yi, center - xi);
449  vec.normalize();
450  vec = scale.xform_point(vec);
451 
452  *p++ = (unsigned char)vec[2];
453  *p++ = (unsigned char)vec[1];
454  *p++ = (unsigned char)vec[0];
455  }
456  }
457 
458  // Page 1: negative X.
459  for (yi = 0; yi < size; ++yi) {
460  for (xi = 0; xi < size; ++xi) {
461  LVector3 vec(-half_size, center - yi, xi - center);
462  vec.normalize();
463  vec = scale.xform_point(vec);
464  *p++ = (unsigned char)vec[2];
465  *p++ = (unsigned char)vec[1];
466  *p++ = (unsigned char)vec[0];
467  }
468  }
469 
470  // Page 2: positive Y.
471  for (yi = 0; yi < size; ++yi) {
472  for (xi = 0; xi < size; ++xi) {
473  LVector3 vec(xi - center, half_size, yi - center);
474  vec.normalize();
475  vec = scale.xform_point(vec);
476  *p++ = (unsigned char)vec[2];
477  *p++ = (unsigned char)vec[1];
478  *p++ = (unsigned char)vec[0];
479  }
480  }
481 
482  // Page 3: negative Y.
483  for (yi = 0; yi < size; ++yi) {
484  for (xi = 0; xi < size; ++xi) {
485  LVector3 vec(xi - center, -half_size, center - yi);
486  vec.normalize();
487  vec = scale.xform_point(vec);
488  *p++ = (unsigned char)vec[2];
489  *p++ = (unsigned char)vec[1];
490  *p++ = (unsigned char)vec[0];
491  }
492  }
493 
494  // Page 4: positive Z.
495  for (yi = 0; yi < size; ++yi) {
496  for (xi = 0; xi < size; ++xi) {
497  LVector3 vec(xi - center, center - yi, half_size);
498  vec.normalize();
499  vec = scale.xform_point(vec);
500  *p++ = (unsigned char)vec[2];
501  *p++ = (unsigned char)vec[1];
502  *p++ = (unsigned char)vec[0];
503  }
504  }
505 
506  // Page 5: negative Z.
507  for (yi = 0; yi < size; ++yi) {
508  for (xi = 0; xi < size; ++xi) {
509  LVector3 vec(center - xi, center - yi, -half_size);
510  vec.normalize();
511  vec = scale.xform_point(vec);
512  *p++ = (unsigned char)vec[2];
513  *p++ = (unsigned char)vec[1];
514  *p++ = (unsigned char)vec[0];
515  }
516  }
517 }
518 
519 /**
520  * Generates a special 256x1 1-d texture that can be used to apply an
521  * arbitrary alpha scale to objects by judicious use of texture matrix. The
522  * texture is a gradient, with an alpha of 0 on the left (U = 0), and 255 on
523  * the right (U = 1).
524  */
525 void Texture::
527  CDWriter cdata(_cycler, true);
528  do_setup_texture(cdata, TT_1d_texture, 256, 1, 1, T_unsigned_byte, F_alpha);
529  cdata->_default_sampler.set_wrap_u(SamplerState::WM_clamp);
530  cdata->_default_sampler.set_minfilter(SamplerState::FT_nearest);
531  cdata->_default_sampler.set_magfilter(SamplerState::FT_nearest);
532 
533  cdata->_compression = CM_off;
534 
535  cdata->inc_image_modified();
536  cdata->inc_properties_modified();
537 
538  PTA_uchar image = do_make_ram_image(cdata);
539  cdata->_keep_ram_image = true;
540 
541  unsigned char *p = image;
542  for (int xi = 0; xi < 256; ++xi) {
543  *p++ = xi;
544  }
545 }
546 
547 /**
548  * Reads the named filename into the texture.
549  */
550 bool Texture::
551 read(const Filename &fullpath, const LoaderOptions &options) {
552  CDWriter cdata(_cycler, true);
553  do_clear(cdata);
554  cdata->inc_properties_modified();
555  cdata->inc_image_modified();
556  return do_read(cdata, fullpath, Filename(), 0, 0, 0, 0, false, false,
557  options, nullptr);
558 }
559 
560 /**
561  * Combine a 3-component image with a grayscale image to get a 4-component
562  * image.
563  *
564  * See the description of the full-parameter read() method for the meaning of
565  * the primary_file_num_channels and alpha_file_channel parameters.
566  */
567 bool Texture::
568 read(const Filename &fullpath, const Filename &alpha_fullpath,
569  int primary_file_num_channels, int alpha_file_channel,
570  const LoaderOptions &options) {
571  CDWriter cdata(_cycler, true);
572  do_clear(cdata);
573  cdata->inc_properties_modified();
574  cdata->inc_image_modified();
575  return do_read(cdata, fullpath, alpha_fullpath, primary_file_num_channels,
576  alpha_file_channel, 0, 0, false, false,
577  options, nullptr);
578 }
579 
580 /**
581  * Reads a single file into a single page or mipmap level, or automatically
582  * reads a series of files into a series of pages and/or mipmap levels.
583  *
584  * See the description of the full-parameter read() method for the meaning of
585  * the various parameters.
586  */
587 bool Texture::
588 read(const Filename &fullpath, int z, int n,
589  bool read_pages, bool read_mipmaps,
590  const LoaderOptions &options) {
591  CDWriter cdata(_cycler, true);
592  cdata->inc_properties_modified();
593  cdata->inc_image_modified();
594  return do_read(cdata, fullpath, Filename(), 0, 0, z, n, read_pages, read_mipmaps,
595  options, nullptr);
596 }
597 
598 /**
599  * Reads the texture from the indicated filename. If
600  * primary_file_num_channels is not 0, it specifies the number of components
601  * to downgrade the image to if it is greater than this number.
602  *
603  * If the filename has the extension .txo, this implicitly reads a texture
604  * object instead of a filename (which replaces all of the texture
605  * properties). In this case, all the rest of the parameters are ignored, and
606  * the filename should not contain any hash marks; just the one named file
607  * will be read, since a single .txo file can contain all pages and mipmaps
608  * necessary to define a texture.
609  *
610  * If alpha_fullpath is not empty, it specifies the name of a file from which
611  * to retrieve the alpha. In this case, alpha_file_channel represents the
612  * numeric channel of this image file to use as the resulting texture's alpha
613  * channel; usually, this is 0 to indicate the grayscale combination of r, g,
614  * b; or it may be a one-based channel number, e.g. 1 for the red channel, 2
615  * for the green channel, and so on.
616  *
617  * If read pages is false, then z indicates the page number into which this
618  * image will be assigned. Normally this is 0 for the first (or only) page of
619  * the texture. 3-D textures have one page for each level of depth, and cube
620  * map textures always have six pages.
621  *
622  * If read_pages is true, multiple images will be read at once, one for each
623  * page of a cube map or a 3-D texture. In this case, the filename should
624  * contain a sequence of one or more hash marks ("#") which will be filled in
625  * with the z value of each page, zero-based. In this case, the z parameter
626  * indicates the maximum z value that will be loaded, or 0 to load all
627  * filenames that exist.
628  *
629  * If read_mipmaps is false, then n indicates the mipmap level to which this
630  * image will be assigned. Normally this is 0 for the base texture image, but
631  * it is possible to load custom mipmap levels into the later images. After
632  * the base texture image is loaded (thus defining the size of the texture),
633  * you can call get_expected_num_mipmap_levels() to determine the maximum
634  * sensible value for n.
635  *
636  * If read_mipmaps is true, multiple images will be read as above, but this
637  * time the images represent the different mipmap levels of the texture image.
638  * In this case, the n parameter indicates the maximum n value that will be
639  * loaded, or 0 to load all filenames that exist (up to the expected number of
640  * mipmap levels).
641  *
642  * If both read_pages and read_mipmaps is true, then both sequences will be
643  * read; the filename should contain two sequences of hash marks, separated by
644  * some character such as a hyphen, underscore, or dot. The first hash mark
645  * sequence will be filled in with the mipmap level, while the second hash
646  * mark sequence will be the page index.
647  *
648  * This method implicitly sets keep_ram_image to false.
649  */
650 bool Texture::
651 read(const Filename &fullpath, const Filename &alpha_fullpath,
652  int primary_file_num_channels, int alpha_file_channel,
653  int z, int n, bool read_pages, bool read_mipmaps,
654  BamCacheRecord *record,
655  const LoaderOptions &options) {
656  CDWriter cdata(_cycler, true);
657  cdata->inc_properties_modified();
658  cdata->inc_image_modified();
659  return do_read(cdata, fullpath, alpha_fullpath, primary_file_num_channels,
660  alpha_file_channel, z, n, read_pages, read_mipmaps,
661  options, record);
662 }
663 
664 /**
665  * Estimates the amount of texture memory that will be consumed by loading
666  * this texture. This returns a value that is not specific to any particular
667  * graphics card or driver; it tries to make a reasonable assumption about how
668  * a driver will load the texture. It does not account for texture
669  * compression or anything fancy. This is mainly useful for debugging and
670  * reporting purposes.
671  *
672  * Returns a value in bytes.
673  */
674 size_t Texture::
676  CDReader cdata(_cycler);
677  size_t pixels = cdata->_x_size * cdata->_y_size * cdata->_z_size;
678 
679  size_t bpp = 4;
680  switch (cdata->_format) {
681  case Texture::F_rgb332:
682  bpp = 1;
683  break;
684 
685  case Texture::F_alpha:
686  case Texture::F_red:
687  case Texture::F_green:
688  case Texture::F_blue:
689  case Texture::F_luminance:
690  case Texture::F_sluminance:
691  case Texture::F_r8i:
692  bpp = 1;
693  break;
694 
695  case Texture::F_luminance_alpha:
696  case Texture::F_luminance_alphamask:
697  case Texture::F_sluminance_alpha:
698  case Texture::F_rgba4:
699  case Texture::F_rgb5:
700  case Texture::F_rgba5:
701  case Texture::F_rg:
702  bpp = 2;
703  break;
704 
705  case Texture::F_rgba:
706  case Texture::F_rgbm:
707  case Texture::F_rgb:
708  case Texture::F_srgb:
709  // Most of the above formats have only 3 bytes, but they are most likely
710  // to get padded by the driver
711  bpp = 4;
712  break;
713 
714  case Texture::F_color_index:
715  case Texture::F_rgb8:
716  case Texture::F_rgba8:
717  case Texture::F_srgb_alpha:
718  case Texture::F_rgb8i:
719  case Texture::F_rgba8i:
720  bpp = 4;
721  break;
722 
723  case Texture::F_depth_stencil:
724  bpp = 4;
725  break;
726 
727  case Texture::F_depth_component:
728  case Texture::F_depth_component16:
729  bpp = 2;
730  break;
731 
732  case Texture::F_depth_component24: // Gets padded
733  case Texture::F_depth_component32:
734  bpp = 4;
735  break;
736 
737  case Texture::F_rgba12:
738  case Texture::F_rgb12:
739  bpp = 8;
740  break;
741 
742  case Texture::F_rgba16:
743  bpp = 8;
744  break;
745  case Texture::F_rgba32:
746  bpp = 16;
747  break;
748 
749  case Texture::F_r16:
750  case Texture::F_r16i:
751  case Texture::F_rg8i:
752  bpp = 2;
753  break;
754  case Texture::F_rg16:
755  bpp = 4;
756  break;
757  case Texture::F_rgb16:
758  bpp = 8;
759  break;
760 
761  case Texture::F_r32i:
762  case Texture::F_r32:
763  bpp = 4;
764  break;
765 
766  case Texture::F_rg32:
767  bpp = 8;
768  break;
769 
770  case Texture::F_rgb32:
771  bpp = 16;
772  break;
773 
774  case Texture::F_r11_g11_b10:
775  case Texture::F_rgb9_e5:
776  case Texture::F_rgb10_a2:
777  bpp = 4;
778  break;
779 
780  default:
781  gobj_cat.warning() << "Unhandled format in estimate_texture_memory(): "
782  << cdata->_format << "\n";
783  break;
784  }
785 
786  size_t bytes = pixels * bpp;
787  if (uses_mipmaps()) {
788  bytes = (bytes * 4) / 3;
789  }
790 
791  return bytes;
792 }
793 
794 /**
795  * Records an arbitrary object in the Texture, associated with a specified
796  * key. The object may later be retrieved by calling get_aux_data() with the
797  * same key.
798  *
799  * These data objects are not recorded to a bam or txo file.
800  */
801 void Texture::
802 set_aux_data(const string &key, TypedReferenceCount *aux_data) {
803  MutexHolder holder(_lock);
804  _aux_data[key] = aux_data;
805 }
806 
807 /**
808  * Removes a record previously recorded via set_aux_data().
809  */
810 void Texture::
811 clear_aux_data(const string &key) {
812  MutexHolder holder(_lock);
813  _aux_data.erase(key);
814 }
815 
816 /**
817  * Returns a record previously recorded via set_aux_data(). Returns NULL if
818  * there was no record associated with the indicated key.
819  */
821 get_aux_data(const string &key) const {
822  MutexHolder holder(_lock);
823  AuxData::const_iterator di;
824  di = _aux_data.find(key);
825  if (di != _aux_data.end()) {
826  return (*di).second;
827  }
828  return nullptr;
829 }
830 
831 /**
832  * Reads the texture from a Panda texture object. This defines the complete
833  * Texture specification, including the image data as well as all texture
834  * properties. This only works if the txo file contains a static Texture
835  * image, as opposed to a subclass of Texture such as a movie texture.
836  *
837  * Pass a real filename if it is available, or empty string if it is not.
838  */
839 bool Texture::
840 read_txo(istream &in, const string &filename) {
841  CDWriter cdata(_cycler, true);
842  cdata->inc_properties_modified();
843  cdata->inc_image_modified();
844  return do_read_txo(cdata, in, filename);
845 }
846 
847 /**
848  * Constructs a new Texture object from the txo file. This is similar to
849  * Texture::read_txo(), but it constructs and returns a new object, which
850  * allows it to return a subclass of Texture (for instance, a movie texture).
851  *
852  * Pass a real filename if it is available, or empty string if it is not.
853  */
854 PT(Texture) Texture::
855 make_from_txo(istream &in, const string &filename) {
856  DatagramInputFile din;
857 
858  if (!din.open(in, filename)) {
859  gobj_cat.error()
860  << "Could not read texture object: " << filename << "\n";
861  return nullptr;
862  }
863 
864  string head;
865  if (!din.read_header(head, _bam_header.size())) {
866  gobj_cat.error()
867  << filename << " is not a texture object file.\n";
868  return nullptr;
869  }
870 
871  if (head != _bam_header) {
872  gobj_cat.error()
873  << filename << " is not a texture object file.\n";
874  return nullptr;
875  }
876 
877  BamReader reader(&din);
878  if (!reader.init()) {
879  return nullptr;
880  }
881 
882  TypedWritable *object = reader.read_object();
883 
884  if (object != nullptr &&
885  object->is_exact_type(BamCacheRecord::get_class_type())) {
886  // Here's a special case: if the first object in the file is a
887  // BamCacheRecord, it's really a cache data file and not a true txo file;
888  // but skip over the cache data record and let the user treat it like an
889  // ordinary txo file.
890  object = reader.read_object();
891  }
892 
893  if (object == nullptr) {
894  gobj_cat.error()
895  << "Texture object " << filename << " is empty.\n";
896  return nullptr;
897 
898  } else if (!object->is_of_type(Texture::get_class_type())) {
899  gobj_cat.error()
900  << "Texture object " << filename << " contains a "
901  << object->get_type() << ", not a Texture.\n";
902  return nullptr;
903  }
904 
905  PT(Texture) other = DCAST(Texture, object);
906  if (!reader.resolve()) {
907  gobj_cat.error()
908  << "Unable to fully resolve texture object file.\n";
909  return nullptr;
910  }
911 
912  return other;
913 }
914 
915 /**
916  * Writes the texture to a Panda texture object. This defines the complete
917  * Texture specification, including the image data as well as all texture
918  * properties.
919  *
920  * The filename is just for reference.
921  */
922 bool Texture::
923 write_txo(ostream &out, const string &filename) const {
924  CDReader cdata(_cycler);
925  return do_write_txo(cdata, out, filename);
926 }
927 
928 /**
929  * Reads the texture from a DDS file object. This is a Microsoft-defined file
930  * format; it is similar in principle to a txo object, in that it is designed
931  * to contain the texture image in a form as similar as possible to its
932  * runtime image, and it can contain mipmaps, pre-compressed textures, and so
933  * on.
934  *
935  * As with read_txo, the filename is just for reference.
936  */
937 bool Texture::
938 read_dds(istream &in, const string &filename, bool header_only) {
939  CDWriter cdata(_cycler, true);
940  cdata->inc_properties_modified();
941  cdata->inc_image_modified();
942  return do_read_dds(cdata, in, filename, header_only);
943 }
944 
945 /**
946  * Reads the texture from a KTX file object. This is a Khronos-defined file
947  * format; it is similar in principle to a dds object, in that it is designed
948  * to contain the texture image in a form as similar as possible to its
949  * runtime image, and it can contain mipmaps, pre-compressed textures, and so
950  * on.
951  *
952  * As with read_dds, the filename is just for reference.
953  */
954 bool Texture::
955 read_ktx(istream &in, const string &filename, bool header_only) {
956  CDWriter cdata(_cycler, true);
957  cdata->inc_properties_modified();
958  cdata->inc_image_modified();
959  return do_read_ktx(cdata, in, filename, header_only);
960 }
961 
962 /**
963  * Loads a texture whose filename is derived by concatenating a suffix to the
964  * filename of this texture. May return NULL, for example, if this texture
965  * doesn't have a filename.
966  */
968 load_related(const InternalName *suffix) const {
969  MutexHolder holder(_lock);
970  CDReader cdata(_cycler);
971 
972  RelatedTextures::const_iterator ti;
973  ti = _related_textures.find(suffix);
974  if (ti != _related_textures.end()) {
975  return (*ti).second;
976  }
977  if (cdata->_fullpath.empty()) {
978  return nullptr;
979  }
980  Filename main = cdata->_fullpath;
981  main.set_basename_wo_extension(main.get_basename_wo_extension() +
982  suffix->get_name());
983  PT(Texture) res;
984  if (!cdata->_alpha_fullpath.empty()) {
985  Filename alph = cdata->_alpha_fullpath;
987  suffix->get_name());
989  if (vfs->exists(alph)) {
990  // The alpha variant of the filename, with the suffix, exists. Use it
991  // to load the texture.
992  res = TexturePool::load_texture(main, alph,
993  cdata->_primary_file_num_channels,
994  cdata->_alpha_file_channel, false);
995  } else {
996  // If the alpha variant of the filename doesn't exist, just go ahead and
997  // load the related texture without alpha.
998  res = TexturePool::load_texture(main);
999  }
1000 
1001  } else {
1002  // No alpha filename--just load the single file. It doesn't necessarily
1003  // have the same number of channels as this one.
1004  res = TexturePool::load_texture(main);
1005  }
1006 
1007  // I'm casting away the const-ness of 'this' because this field is only a
1008  // cache.
1009  ((Texture *)this)->_related_textures.insert(RelatedTextures::value_type(suffix, res));
1010  return res;
1011 }
1012 
1013 /**
1014  * Replaces the current system-RAM image with the new data, converting it
1015  * first if necessary from the indicated component-order format. See
1016  * get_ram_image_as() for specifications about the format. This method cannot
1017  * support compressed image data or sub-pages; use set_ram_image() for that.
1018  */
1019 void Texture::
1020 set_ram_image_as(CPTA_uchar image, const string &supplied_format) {
1021  CDWriter cdata(_cycler, true);
1022 
1023  string format = upcase(supplied_format);
1024 
1025  // Make sure we can grab something that's uncompressed.
1026  int imgsize = cdata->_x_size * cdata->_y_size;
1027  nassertv(image.size() == (size_t)(cdata->_component_width * format.size() * imgsize));
1028 
1029  // Check if the format is already what we have internally.
1030  if ((cdata->_num_components == 1 && format.size() == 1) ||
1031  (cdata->_num_components == 2 && format.size() == 2 && format.at(1) == 'A' && format.at(0) != 'A') ||
1032  (cdata->_num_components == 3 && format == "BGR") ||
1033  (cdata->_num_components == 4 && format == "BGRA")) {
1034  // The format string is already our format, so we just need to copy it.
1035  do_set_ram_image(cdata, image);
1036  return;
1037  }
1038 
1039  // Create a new empty array that can hold our image.
1040  PTA_uchar newdata = PTA_uchar::empty_array(imgsize * cdata->_num_components * cdata->_component_width, get_class_type());
1041 
1042  // These ifs are for optimization of commonly used image types.
1043  if (cdata->_component_width == 1) {
1044  if (format == "RGBA" && cdata->_num_components == 4) {
1045  imgsize *= 4;
1046  for (int p = 0; p < imgsize; p += 4) {
1047  newdata[p + 2] = image[p ];
1048  newdata[p + 1] = image[p + 1];
1049  newdata[p ] = image[p + 2];
1050  newdata[p + 3] = image[p + 3];
1051  }
1052  do_set_ram_image(cdata, newdata);
1053  return;
1054  }
1055  if (format == "RGB" && cdata->_num_components == 3) {
1056  imgsize *= 3;
1057  for (int p = 0; p < imgsize; p += 3) {
1058  newdata[p + 2] = image[p ];
1059  newdata[p + 1] = image[p + 1];
1060  newdata[p ] = image[p + 2];
1061  }
1062  do_set_ram_image(cdata, newdata);
1063  return;
1064  }
1065  if (format == "A" && cdata->_num_components != 3) {
1066  // We can generally rely on alpha to be the last component.
1067  int component = cdata->_num_components - 1;
1068  for (int p = 0; p < imgsize; ++p) {
1069  newdata[component] = image[p];
1070  }
1071  do_set_ram_image(cdata, newdata);
1072  return;
1073  }
1074  for (int p = 0; p < imgsize; ++p) {
1075  for (uchar s = 0; s < format.size(); ++s) {
1076  signed char component = -1;
1077  if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
1078  component = 0;
1079  } else if (format.at(s) == 'G') {
1080  component = 1;
1081  } else if (format.at(s) == 'R') {
1082  component = 2;
1083  } else if (format.at(s) == 'A') {
1084  if (cdata->_num_components != 3) {
1085  component = cdata->_num_components - 1;
1086  } else {
1087  // Ignore.
1088  }
1089  } else if (format.at(s) == '0') {
1090  // Ignore.
1091  } else if (format.at(s) == '1') {
1092  // Ignore.
1093  } else {
1094  gobj_cat.error() << "Unexpected component character '"
1095  << format.at(s) << "', expected one of RGBA!\n";
1096  return;
1097  }
1098  if (component >= 0) {
1099  newdata[p * cdata->_num_components + component] = image[p * format.size() + s];
1100  }
1101  }
1102  }
1103  do_set_ram_image(cdata, newdata);
1104  return;
1105  }
1106  for (int p = 0; p < imgsize; ++p) {
1107  for (uchar s = 0; s < format.size(); ++s) {
1108  signed char component = -1;
1109  if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
1110  component = 0;
1111  } else if (format.at(s) == 'G') {
1112  component = 1;
1113  } else if (format.at(s) == 'R') {
1114  component = 2;
1115  } else if (format.at(s) == 'A') {
1116  if (cdata->_num_components != 3) {
1117  component = cdata->_num_components - 1;
1118  } else {
1119  // Ignore.
1120  }
1121  } else if (format.at(s) == '0') {
1122  // Ignore.
1123  } else if (format.at(s) == '1') {
1124  // Ignore.
1125  } else {
1126  gobj_cat.error() << "Unexpected component character '"
1127  << format.at(s) << "', expected one of RGBA!\n";
1128  return;
1129  }
1130  if (component >= 0) {
1131  memcpy((void*)(newdata + (p * cdata->_num_components + component) * cdata->_component_width),
1132  (void*)(image + (p * format.size() + s) * cdata->_component_width),
1133  cdata->_component_width);
1134  }
1135  }
1136  }
1137  do_set_ram_image(cdata, newdata);
1138  return;
1139 }
1140 
1141 /**
1142  * Returns the flag that indicates whether this Texture is eligible to have
1143  * its main RAM copy of the texture memory dumped when the texture is prepared
1144  * for rendering. See set_keep_ram_image().
1145  */
1146 bool Texture::
1147 get_keep_ram_image() const {
1148  CDReader cdata(_cycler);
1149  return cdata->_keep_ram_image;
1150 }
1151 
1152 /**
1153  * Returns true if there is enough information in this Texture object to write
1154  * it to the bam cache successfully, false otherwise. For most textures, this
1155  * is the same as has_ram_image().
1156  */
1157 bool Texture::
1158 is_cacheable() const {
1159  CDReader cdata(_cycler);
1160  return do_has_bam_rawdata(cdata);
1161 }
1162 
1163 /**
1164  * Returns the number of contiguous mipmap levels that exist in RAM, up until
1165  * the first gap in the sequence. It is guaranteed that at least mipmap
1166  * levels [0, get_num_ram_mipmap_images()) exist.
1167  *
1168  * The number returned will never exceed the number of required mipmap images
1169  * based on the size of the texture and its filter mode.
1170  *
1171  * This method is different from get_num_ram_mipmap_images() in that it
1172  * returns only the number of mipmap levels that can actually be usefully
1173  * loaded, regardless of the actual number that may be stored.
1174  */
1175 int Texture::
1177  CDReader cdata(_cycler);
1178  if (cdata->_ram_images.empty() || cdata->_ram_images[0]._image.empty()) {
1179  // If we don't even have a base image, the answer is none.
1180  return 0;
1181  }
1182  if (!uses_mipmaps()) {
1183  // If we have a base image and don't require mipmapping, the answer is 1.
1184  return 1;
1185  }
1186 
1187  // Check that we have enough mipmap levels to meet the size requirements.
1188  int size = max(cdata->_x_size, max(cdata->_y_size, cdata->_z_size));
1189  int n = 0;
1190  int x = 1;
1191  while (x < size) {
1192  x = (x << 1);
1193  ++n;
1194  if (n >= (int)cdata->_ram_images.size() || cdata->_ram_images[n]._image.empty()) {
1195  return n;
1196  }
1197  }
1198 
1199  ++n;
1200  return n;
1201 }
1202 
1203 /**
1204  * Returns the system-RAM image data associated with the nth mipmap level, if
1205  * present. Returns NULL if the nth mipmap level is not present.
1206  */
1208 get_ram_mipmap_image(int n) const {
1209  CDReader cdata(_cycler);
1210  if (n < (int)cdata->_ram_images.size() && !cdata->_ram_images[n]._image.empty()) {
1211  return cdata->_ram_images[n]._image;
1212  }
1213  return CPTA_uchar(get_class_type());
1214 }
1215 
1216 /**
1217  * Similiar to get_ram_mipmap_image(), however, in this case the void pointer
1218  * for the given ram image is returned. This will be NULL unless it has been
1219  * explicitly set.
1220  */
1221 void *Texture::
1223  CDReader cdata(_cycler);
1224  if (n < (int)cdata->_ram_images.size()) {
1225  return cdata->_ram_images[n]._pointer_image;
1226  }
1227  return nullptr;
1228 }
1229 
1230 /**
1231  * Sets an explicit void pointer as the texture's mipmap image for the
1232  * indicated level. This is a special call to direct a texture to reference
1233  * some external image location, for instance from a webcam input.
1234  *
1235  * The texture will henceforth reference this pointer directly, instead of its
1236  * own internal storage; the user is responsible for ensuring the data at this
1237  * address remains allocated and valid, and in the correct format, during the
1238  * lifetime of the texture.
1239  */
1240 void Texture::
1241 set_ram_mipmap_pointer(int n, void *image, size_t page_size) {
1242  CDWriter cdata(_cycler, true);
1243  nassertv(cdata->_ram_image_compression != CM_off || do_get_expected_ram_mipmap_image_size(cdata, n));
1244 
1245  while (n >= (int)cdata->_ram_images.size()) {
1246  cdata->_ram_images.push_back(RamImage());
1247  }
1248 
1249  cdata->_ram_images[n]._page_size = page_size;
1250  // _ram_images[n]._image.clear(); wtf is going on?!
1251  cdata->_ram_images[n]._pointer_image = image;
1252  cdata->inc_image_modified();
1253 }
1254 
1255 /**
1256  * Accepts a raw pointer cast as an int, which is then passed to
1257  * set_ram_mipmap_pointer(); see the documentation for that method.
1258  *
1259  * This variant is particularly useful to set an external pointer from a
1260  * language like Python, which doesn't support void pointers directly.
1261  */
1262 void Texture::
1263 set_ram_mipmap_pointer_from_int(long long pointer, int n, int page_size) {
1264  set_ram_mipmap_pointer(n, (void*)pointer, (size_t)page_size);
1265 }
1266 
1267 /**
1268  * Discards the current system-RAM image for the nth mipmap level.
1269  */
1270 void Texture::
1272  CDWriter cdata(_cycler, true);
1273  if (n >= (int)cdata->_ram_images.size()) {
1274  return;
1275  }
1276  cdata->_ram_images[n]._page_size = 0;
1277  cdata->_ram_images[n]._image.clear();
1278  cdata->_ram_images[n]._pointer_image = nullptr;
1279 }
1280 
1281 /**
1282  * Returns a modifiable pointer to the internal "simple" texture image. See
1283  * set_simple_ram_image().
1284  */
1285 PTA_uchar Texture::
1287  CDWriter cdata(_cycler, true);
1288  cdata->_simple_image_date_generated = (int32_t)time(nullptr);
1289  return cdata->_simple_ram_image._image;
1290 }
1291 
1292 /**
1293  * Creates an empty array for the simple ram image of the indicated size, and
1294  * returns a modifiable pointer to the new array. See set_simple_ram_image().
1295  */
1296 PTA_uchar Texture::
1297 new_simple_ram_image(int x_size, int y_size) {
1298  CDWriter cdata(_cycler, true);
1299  nassertr(cdata->_texture_type == TT_2d_texture, PTA_uchar());
1300  size_t expected_page_size = (size_t)(x_size * y_size * 4);
1301 
1302  cdata->_simple_x_size = x_size;
1303  cdata->_simple_y_size = y_size;
1304  cdata->_simple_ram_image._image = PTA_uchar::empty_array(expected_page_size);
1305  cdata->_simple_ram_image._page_size = expected_page_size;
1306  cdata->_simple_image_date_generated = (int32_t)time(nullptr);
1307  cdata->inc_simple_image_modified();
1308 
1309  return cdata->_simple_ram_image._image;
1310 }
1311 
1312 /**
1313  * Computes the "simple" ram image by loading the main RAM image, if it is not
1314  * already available, and reducing it to 16x16 or smaller. This may be an
1315  * expensive operation.
1316  */
1317 void Texture::
1319  CDWriter cdata(_cycler, true);
1320 
1321  if (cdata->_texture_type != TT_2d_texture ||
1322  cdata->_ram_image_compression != CM_off) {
1323  return;
1324  }
1325 
1326  PNMImage pnmimage;
1327  if (!do_store_one(cdata, pnmimage, 0, 0)) {
1328  return;
1329  }
1330 
1331  // Start at the suggested size from the config file.
1332  int x_size = simple_image_size.get_word(0);
1333  int y_size = simple_image_size.get_word(1);
1334 
1335  // Limit it to no larger than the source image, and also make it a power of
1336  // two.
1337  x_size = down_to_power_2(min(x_size, cdata->_x_size));
1338  y_size = down_to_power_2(min(y_size, cdata->_y_size));
1339 
1340  // Generate a reduced image of that size.
1341  PNMImage scaled(x_size, y_size, pnmimage.get_num_channels());
1342  scaled.quick_filter_from(pnmimage);
1343 
1344  // Make sure the reduced image has 4 components, by convention.
1345  if (!scaled.has_alpha()) {
1346  scaled.add_alpha();
1347  scaled.alpha_fill(1.0);
1348  }
1349  scaled.set_num_channels(4);
1350 
1351  // Now see if we can go even smaller.
1352  bool did_anything;
1353  do {
1354  did_anything = false;
1355 
1356  // Try to reduce X.
1357  if (x_size > 1) {
1358  int new_x_size = (x_size >> 1);
1359  PNMImage smaller(new_x_size, y_size, 4);
1360  smaller.quick_filter_from(scaled);
1361  PNMImage bigger(x_size, y_size, 4);
1362  bigger.quick_filter_from(smaller);
1363 
1364  if (compare_images(scaled, bigger)) {
1365  scaled.take_from(smaller);
1366  x_size = new_x_size;
1367  did_anything = true;
1368  }
1369  }
1370 
1371  // Try to reduce Y.
1372  if (y_size > 1) {
1373  int new_y_size = (y_size >> 1);
1374  PNMImage smaller(x_size, new_y_size, 4);
1375  smaller.quick_filter_from(scaled);
1376  PNMImage bigger(x_size, y_size, 4);
1377  bigger.quick_filter_from(smaller);
1378 
1379  if (compare_images(scaled, bigger)) {
1380  scaled.take_from(smaller);
1381  y_size = new_y_size;
1382  did_anything = true;
1383  }
1384  }
1385  } while (did_anything);
1386 
1387  size_t expected_page_size = (size_t)(x_size * y_size * 4);
1388  PTA_uchar image = PTA_uchar::empty_array(expected_page_size, get_class_type());
1389  convert_from_pnmimage(image, expected_page_size, x_size, 0, 0, 0, scaled, 4, 1);
1390 
1391  do_set_simple_ram_image(cdata, image, x_size, y_size);
1392  cdata->_simple_image_date_generated = (int32_t)time(nullptr);
1393 }
1394 
1395 /**
1396  * Returns a TexturePeeker object that can be used to examine the individual
1397  * texels stored within this Texture by (u, v) coordinate.
1398  *
1399  * If the texture has a ram image resident, that image is used. If it does
1400  * not have a full ram image but does have a simple_ram_image resident, that
1401  * image is used instead. If neither image is resident the full image is
1402  * reloaded.
1403  *
1404  * Returns NULL if the texture cannot find an image to load, or the texture
1405  * format is incompatible.
1406  */
1407 PT(TexturePeeker) Texture::
1408 peek() {
1409  CDWriter cdata(_cycler, unlocked_ensure_ram_image(true));
1410 
1411  PT(TexturePeeker) peeker = new TexturePeeker(this, cdata);
1412  if (peeker->is_valid()) {
1413  return peeker;
1414  }
1415 
1416  return nullptr;
1417 }
1418 
1419 /**
1420  * Indicates that the texture should be enqueued to be prepared in the
1421  * indicated prepared_objects at the beginning of the next frame. This will
1422  * ensure the texture is already loaded into texture memory if it is expected
1423  * to be rendered soon.
1424  *
1425  * Use this function instead of prepare_now() to preload textures from a user
1426  * interface standpoint.
1427  */
1428 PT(AsyncFuture) Texture::
1429 prepare(PreparedGraphicsObjects *prepared_objects) {
1430  return prepared_objects->enqueue_texture_future(this);
1431 }
1432 
1433 /**
1434  * Returns true if the texture has already been prepared or enqueued for
1435  * preparation on the indicated GSG, false otherwise.
1436  */
1437 bool Texture::
1438 is_prepared(PreparedGraphicsObjects *prepared_objects) const {
1439  MutexHolder holder(_lock);
1440  PreparedViews::const_iterator pvi;
1441  pvi = _prepared_views.find(prepared_objects);
1442  if (pvi != _prepared_views.end()) {
1443  return true;
1444  }
1445  return prepared_objects->is_texture_queued(this);
1446 }
1447 
1448 /**
1449  * Returns true if the texture needs to be re-loaded onto the indicated GSG,
1450  * either because its image data is out-of-date, or because it's not fully
1451  * prepared now.
1452  */
1453 bool Texture::
1455  MutexHolder holder(_lock);
1456  CDReader cdata(_cycler);
1457 
1458  PreparedViews::const_iterator pvi;
1459  pvi = _prepared_views.find(prepared_objects);
1460  if (pvi != _prepared_views.end()) {
1461  const Contexts &contexts = (*pvi).second;
1462  for (int view = 0; view < cdata->_num_views; ++view) {
1463  Contexts::const_iterator ci;
1464  ci = contexts.find(view);
1465  if (ci == contexts.end()) {
1466  return true;
1467  }
1468  TextureContext *tc = (*ci).second;
1469  if (tc->was_image_modified()) {
1470  return true;
1471  }
1472  }
1473  return false;
1474  }
1475  return true;
1476 }
1477 
1478 /**
1479  * Returns the number of bytes which the texture is reported to consume within
1480  * graphics memory, for the indicated GSG. This may return a nonzero value
1481  * even if the texture is not currently resident; you should also check
1482  * get_resident() if you want to know how much space the texture is actually
1483  * consuming right now.
1484  */
1485 size_t Texture::
1487  MutexHolder holder(_lock);
1488  CDReader cdata(_cycler);
1489 
1490  PreparedViews::const_iterator pvi;
1491  size_t total_size = 0;
1492  pvi = _prepared_views.find(prepared_objects);
1493  if (pvi != _prepared_views.end()) {
1494  const Contexts &contexts = (*pvi).second;
1495  for (int view = 0; view < cdata->_num_views; ++view) {
1496  Contexts::const_iterator ci;
1497  ci = contexts.find(view);
1498  if (ci != contexts.end()) {
1499  TextureContext *tc = (*ci).second;
1500  total_size += tc->get_data_size_bytes();
1501  }
1502  }
1503  }
1504 
1505  return total_size;
1506 }
1507 
1508 /**
1509  * Returns true if this Texture was rendered in the most recent frame within
1510  * the indicated GSG.
1511  */
1512 bool Texture::
1513 get_active(PreparedGraphicsObjects *prepared_objects) const {
1514  MutexHolder holder(_lock);
1515  CDReader cdata(_cycler);
1516 
1517  PreparedViews::const_iterator pvi;
1518  pvi = _prepared_views.find(prepared_objects);
1519  if (pvi != _prepared_views.end()) {
1520  const Contexts &contexts = (*pvi).second;
1521  for (int view = 0; view < cdata->_num_views; ++view) {
1522  Contexts::const_iterator ci;
1523  ci = contexts.find(view);
1524  if (ci != contexts.end()) {
1525  TextureContext *tc = (*ci).second;
1526  if (tc->get_active()) {
1527  return true;
1528  }
1529  }
1530  }
1531  }
1532  return false;
1533 }
1534 
1535 /**
1536  * Returns true if this Texture is reported to be resident within graphics
1537  * memory for the indicated GSG.
1538  */
1539 bool Texture::
1540 get_resident(PreparedGraphicsObjects *prepared_objects) const {
1541  MutexHolder holder(_lock);
1542  CDReader cdata(_cycler);
1543 
1544  PreparedViews::const_iterator pvi;
1545  pvi = _prepared_views.find(prepared_objects);
1546  if (pvi != _prepared_views.end()) {
1547  const Contexts &contexts = (*pvi).second;
1548  for (int view = 0; view < cdata->_num_views; ++view) {
1549  Contexts::const_iterator ci;
1550  ci = contexts.find(view);
1551  if (ci != contexts.end()) {
1552  TextureContext *tc = (*ci).second;
1553  if (tc->get_resident()) {
1554  return true;
1555  }
1556  }
1557  }
1558  }
1559  return false;
1560 }
1561 
1562 /**
1563  * Frees the texture context only on the indicated object, if it exists there.
1564  * Returns true if it was released, false if it had not been prepared.
1565  */
1566 bool Texture::
1567 release(PreparedGraphicsObjects *prepared_objects) {
1568  MutexHolder holder(_lock);
1569  PreparedViews::iterator pvi;
1570  pvi = _prepared_views.find(prepared_objects);
1571  if (pvi != _prepared_views.end()) {
1572  Contexts temp;
1573  temp.swap((*pvi).second);
1574  Contexts::iterator ci;
1575  for (ci = temp.begin(); ci != temp.end(); ++ci) {
1576  TextureContext *tc = (*ci).second;
1577  if (tc != nullptr) {
1578  prepared_objects->release_texture(tc);
1579  }
1580  }
1581  _prepared_views.erase(pvi);
1582  }
1583 
1584  // Maybe it wasn't prepared yet, but it's about to be.
1585  return prepared_objects->dequeue_texture(this);
1586 }
1587 
1588 /**
1589  * Frees the context allocated on all objects for which the texture has been
1590  * declared. Returns the number of contexts which have been freed.
1591  */
1592 int Texture::
1594  MutexHolder holder(_lock);
1595 
1596  // We have to traverse a copy of the _prepared_views list, because the
1597  // PreparedGraphicsObjects object will call clear_prepared() in response to
1598  // each release_texture(), and we don't want to be modifying the
1599  // _prepared_views list while we're traversing it.
1600  PreparedViews temp;
1601  temp.swap(_prepared_views);
1602  int num_freed = (int)temp.size();
1603 
1604  PreparedViews::iterator pvi;
1605  for (pvi = temp.begin(); pvi != temp.end(); ++pvi) {
1606  PreparedGraphicsObjects *prepared_objects = (*pvi).first;
1607  Contexts temp;
1608  temp.swap((*pvi).second);
1609  Contexts::iterator ci;
1610  for (ci = temp.begin(); ci != temp.end(); ++ci) {
1611  TextureContext *tc = (*ci).second;
1612  if (tc != nullptr) {
1613  prepared_objects->release_texture(tc);
1614  }
1615  }
1616  }
1617 
1618  return num_freed;
1619 }
1620 
1621 /**
1622  * Not to be confused with write(Filename), this method simply describes the
1623  * texture properties.
1624  */
1625 void Texture::
1626 write(ostream &out, int indent_level) const {
1627  CDReader cdata(_cycler);
1628  indent(out, indent_level)
1629  << cdata->_texture_type << " " << get_name();
1630  if (!cdata->_filename.empty()) {
1631  out << " (from " << cdata->_filename << ")";
1632  }
1633  out << "\n";
1634 
1635  indent(out, indent_level + 2);
1636 
1637  switch (cdata->_texture_type) {
1638  case TT_1d_texture:
1639  out << "1-d, " << cdata->_x_size;
1640  break;
1641 
1642  case TT_2d_texture:
1643  out << "2-d, " << cdata->_x_size << " x " << cdata->_y_size;
1644  break;
1645 
1646  case TT_3d_texture:
1647  out << "3-d, " << cdata->_x_size << " x " << cdata->_y_size << " x " << cdata->_z_size;
1648  break;
1649 
1650  case TT_2d_texture_array:
1651  out << "2-d array, " << cdata->_x_size << " x " << cdata->_y_size << " x " << cdata->_z_size;
1652  break;
1653 
1654  case TT_cube_map:
1655  out << "cube map, " << cdata->_x_size << " x " << cdata->_y_size;
1656  break;
1657 
1658  case TT_cube_map_array:
1659  out << "cube map array, " << cdata->_x_size << " x " << cdata->_y_size << " x " << cdata->_z_size;
1660  break;
1661 
1662  case TT_buffer_texture:
1663  out << "buffer, " << cdata->_x_size;
1664  break;
1665 
1666  case TT_1d_texture_array:
1667  out << "1-d array, " << cdata->_x_size << " x " << cdata->_y_size;
1668  break;
1669  }
1670 
1671  if (cdata->_num_views > 1) {
1672  out << " (x " << cdata->_num_views << " views)";
1673  }
1674 
1675  out << " pixels, each " << cdata->_num_components;
1676 
1677  switch (cdata->_component_type) {
1678  case T_unsigned_byte:
1679  case T_byte:
1680  out << " bytes";
1681  break;
1682 
1683  case T_unsigned_short:
1684  case T_short:
1685  out << " shorts";
1686  break;
1687 
1688  case T_half_float:
1689  out << " half";
1690  case T_float:
1691  out << " floats";
1692  break;
1693 
1694  case T_unsigned_int_24_8:
1695  case T_int:
1696  case T_unsigned_int:
1697  out << " ints";
1698  break;
1699 
1700  default:
1701  break;
1702  }
1703 
1704  out << ", ";
1705  switch (cdata->_format) {
1706  case F_color_index:
1707  out << "color_index";
1708  break;
1709  case F_depth_stencil:
1710  out << "depth_stencil";
1711  break;
1712  case F_depth_component:
1713  out << "depth_component";
1714  break;
1715  case F_depth_component16:
1716  out << "depth_component16";
1717  break;
1718  case F_depth_component24:
1719  out << "depth_component24";
1720  break;
1721  case F_depth_component32:
1722  out << "depth_component32";
1723  break;
1724 
1725  case F_rgba:
1726  out << "rgba";
1727  break;
1728  case F_rgbm:
1729  out << "rgbm";
1730  break;
1731  case F_rgba32:
1732  out << "rgba32";
1733  break;
1734  case F_rgba16:
1735  out << "rgba16";
1736  break;
1737  case F_rgba12:
1738  out << "rgba12";
1739  break;
1740  case F_rgba8:
1741  out << "rgba8";
1742  break;
1743  case F_rgba4:
1744  out << "rgba4";
1745  break;
1746 
1747  case F_rgb:
1748  out << "rgb";
1749  break;
1750  case F_rgb12:
1751  out << "rgb12";
1752  break;
1753  case F_rgb8:
1754  out << "rgb8";
1755  break;
1756  case F_rgb5:
1757  out << "rgb5";
1758  break;
1759  case F_rgba5:
1760  out << "rgba5";
1761  break;
1762  case F_rgb332:
1763  out << "rgb332";
1764  break;
1765 
1766  case F_red:
1767  out << "red";
1768  break;
1769  case F_green:
1770  out << "green";
1771  break;
1772  case F_blue:
1773  out << "blue";
1774  break;
1775  case F_alpha:
1776  out << "alpha";
1777  break;
1778  case F_luminance:
1779  out << "luminance";
1780  break;
1781  case F_luminance_alpha:
1782  out << "luminance_alpha";
1783  break;
1784  case F_luminance_alphamask:
1785  out << "luminance_alphamask";
1786  break;
1787 
1788  case F_r16:
1789  out << "r16";
1790  break;
1791  case F_r16i:
1792  out << "r16i";
1793  break;
1794 
1795  case F_rg16:
1796  out << "rg16";
1797  break;
1798  case F_rgb16:
1799  out << "rgb16";
1800  break;
1801 
1802  case F_srgb:
1803  out << "srgb";
1804  break;
1805  case F_srgb_alpha:
1806  out << "srgb_alpha";
1807  break;
1808  case F_sluminance:
1809  out << "sluminance";
1810  break;
1811  case F_sluminance_alpha:
1812  out << "sluminance_alpha";
1813  break;
1814 
1815  case F_r32i:
1816  out << "r32i";
1817  break;
1818 
1819  case F_r32:
1820  out << "r32";
1821  break;
1822  case F_rg32:
1823  out << "rg32";
1824  break;
1825  case F_rgb32:
1826  out << "rgb32";
1827  break;
1828 
1829  case F_r8i:
1830  out << "r8i";
1831  break;
1832  case F_rg8i:
1833  out << "rg8i";
1834  break;
1835  case F_rgb8i:
1836  out << "rgb8i";
1837  break;
1838  case F_rgba8i:
1839  out << "rgba8i";
1840  break;
1841  case F_r11_g11_b10:
1842  out << "r11_g11_b10";
1843  break;
1844  case F_rgb9_e5:
1845  out << "rgb9_e5";
1846  break;
1847  case F_rgb10_a2:
1848  out << "rgb10_a2";
1849  break;
1850 
1851  case F_rg:
1852  out << "rg";
1853  break;
1854  }
1855 
1856  if (cdata->_compression != CM_default) {
1857  out << ", compression " << cdata->_compression;
1858  }
1859  out << "\n";
1860 
1861  indent(out, indent_level + 2);
1862 
1863  cdata->_default_sampler.output(out);
1864 
1865  if (do_has_ram_image(cdata)) {
1866  indent(out, indent_level + 2)
1867  << do_get_ram_image_size(cdata) << " bytes in ram, compression "
1868  << cdata->_ram_image_compression << "\n";
1869 
1870  if (cdata->_ram_images.size() > 1) {
1871  int count = 0;
1872  size_t total_size = 0;
1873  for (size_t n = 1; n < cdata->_ram_images.size(); ++n) {
1874  if (!cdata->_ram_images[n]._image.empty()) {
1875  ++count;
1876  total_size += cdata->_ram_images[n]._image.size();
1877  } else {
1878  // Stop at the first gap.
1879  break;
1880  }
1881  }
1882  indent(out, indent_level + 2)
1883  << count
1884  << " mipmap levels also present in ram (" << total_size
1885  << " bytes).\n";
1886  }
1887 
1888  } else {
1889  indent(out, indent_level + 2)
1890  << "no ram image\n";
1891  }
1892 
1893  if (!cdata->_simple_ram_image._image.empty()) {
1894  indent(out, indent_level + 2)
1895  << "simple image: " << cdata->_simple_x_size << " x "
1896  << cdata->_simple_y_size << ", "
1897  << cdata->_simple_ram_image._image.size() << " bytes\n";
1898  }
1899 }
1900 
1901 
1902 /**
1903  * Changes the size of the texture, padding if necessary, and setting the pad
1904  * region as well.
1905  */
1906 void Texture::
1907 set_size_padded(int x, int y, int z) {
1908  CDWriter cdata(_cycler, true);
1909  if (do_get_auto_texture_scale(cdata) != ATS_none) {
1910  do_set_x_size(cdata, up_to_power_2(x));
1911  do_set_y_size(cdata, up_to_power_2(y));
1912 
1913  if (cdata->_texture_type == TT_3d_texture) {
1914  // Only pad 3D textures. It does not make sense to do so for cube maps
1915  // or 2D texture arrays.
1916  do_set_z_size(cdata, up_to_power_2(z));
1917  } else {
1918  do_set_z_size(cdata, z);
1919  }
1920  } else {
1921  do_set_x_size(cdata, x);
1922  do_set_y_size(cdata, y);
1923  do_set_z_size(cdata, z);
1924  }
1925  do_set_pad_size(cdata,
1926  cdata->_x_size - x,
1927  cdata->_y_size - y,
1928  cdata->_z_size - z);
1929 }
1930 
1931 /**
1932  * Specifies the size of the texture as it exists in its original disk file,
1933  * before any Panda scaling.
1934  */
1935 void Texture::
1936 set_orig_file_size(int x, int y, int z) {
1937  CDWriter cdata(_cycler, true);
1938  cdata->_orig_file_x_size = x;
1939  cdata->_orig_file_y_size = y;
1940 
1941  nassertv(z == cdata->_z_size);
1942 }
1943 
1944 /**
1945  * Creates a context for the texture on the particular GSG, if it does not
1946  * already exist. Returns the new (or old) TextureContext. This assumes that
1947  * the GraphicsStateGuardian is the currently active rendering context and
1948  * that it is ready to accept new textures. If this is not necessarily the
1949  * case, you should use prepare() instead.
1950  *
1951  * Normally, this is not called directly except by the GraphicsStateGuardian;
1952  * a texture does not need to be explicitly prepared by the user before it may
1953  * be rendered.
1954  */
1956 prepare_now(int view,
1957  PreparedGraphicsObjects *prepared_objects,
1959  MutexHolder holder(_lock);
1960  CDReader cdata(_cycler);
1961 
1962  // Don't exceed the actual number of views.
1963  view = max(min(view, cdata->_num_views - 1), 0);
1964 
1965  // Get the list of PreparedGraphicsObjects for this view.
1966  Contexts &contexts = _prepared_views[prepared_objects];
1967  Contexts::const_iterator pvi;
1968  pvi = contexts.find(view);
1969  if (pvi != contexts.end()) {
1970  return (*pvi).second;
1971  }
1972 
1973  TextureContext *tc = prepared_objects->prepare_texture_now(this, view, gsg);
1974  contexts[view] = tc;
1975 
1976  return tc;
1977 }
1978 
1979 /**
1980  * Returns the smallest power of 2 greater than or equal to value.
1981  */
1982 int Texture::
1983 up_to_power_2(int value) {
1984  if (value <= 1) {
1985  return 1;
1986  }
1987  int bit = get_next_higher_bit(((unsigned int)value) - 1);
1988  return (1 << bit);
1989 }
1990 
1991 /**
1992  * Returns the largest power of 2 less than or equal to value.
1993  */
1994 int Texture::
1995 down_to_power_2(int value) {
1996  if (value <= 1) {
1997  return 1;
1998  }
1999  int bit = get_next_higher_bit(((unsigned int)value) >> 1);
2000  return (1 << bit);
2001 }
2002 
2003 /**
2004  * Asks the PNMImage to change its scale when it reads the image, according to
2005  * the whims of the Config.prc file.
2006  *
2007  * For most efficient results, this method should be called after
2008  * pnmimage.read_header() has been called, but before pnmimage.read(). This
2009  * method may also be called after pnmimage.read(), i.e. when the pnmimage is
2010  * already loaded; in this case it will rescale the image on the spot. Also
2011  * see rescale_texture().
2012  */
2013 void Texture::
2015  consider_rescale(pnmimage, get_name(), get_auto_texture_scale());
2016 }
2017 
2018 /**
2019  * Asks the PNMImage to change its scale when it reads the image, according to
2020  * the whims of the Config.prc file.
2021  *
2022  * For most efficient results, this method should be called after
2023  * pnmimage.read_header() has been called, but before pnmimage.read(). This
2024  * method may also be called after pnmimage.read(), i.e. when the pnmimage is
2025  * already loaded; in this case it will rescale the image on the spot. Also
2026  * see rescale_texture().
2027  */
2028 void Texture::
2029 consider_rescale(PNMImage &pnmimage, const string &name, AutoTextureScale auto_texture_scale) {
2030  int new_x_size = pnmimage.get_x_size();
2031  int new_y_size = pnmimage.get_y_size();
2032  if (adjust_size(new_x_size, new_y_size, name, false, auto_texture_scale)) {
2033  if (pnmimage.is_valid()) {
2034  // The image is already loaded. Rescale on the spot.
2035  PNMImage new_image(new_x_size, new_y_size, pnmimage.get_num_channels(),
2036  pnmimage.get_maxval(), pnmimage.get_type(),
2037  pnmimage.get_color_space());
2038  new_image.quick_filter_from(pnmimage);
2039  pnmimage.take_from(new_image);
2040  } else {
2041  // Rescale while reading. Some image types (e.g. jpeg) can take
2042  // advantage of this.
2043  pnmimage.set_read_size(new_x_size, new_y_size);
2044  }
2045  }
2046 }
2047 
2048 /**
2049  * Returns the indicated TextureType converted to a string word.
2050  */
2051 string Texture::
2052 format_texture_type(TextureType tt) {
2053  switch (tt) {
2054  case TT_1d_texture:
2055  return "1d_texture";
2056  case TT_2d_texture:
2057  return "2d_texture";
2058  case TT_3d_texture:
2059  return "3d_texture";
2060  case TT_2d_texture_array:
2061  return "2d_texture_array";
2062  case TT_cube_map:
2063  return "cube_map";
2064  case TT_cube_map_array:
2065  return "cube_map_array";
2066  case TT_buffer_texture:
2067  return "buffer_texture";
2068  case TT_1d_texture_array:
2069  return "1d_texture_array";
2070  }
2071  return "**invalid**";
2072 }
2073 
2074 /**
2075  * Returns the TextureType corresponding to the indicated string word.
2076  */
2077 Texture::TextureType Texture::
2078 string_texture_type(const string &str) {
2079  if (cmp_nocase(str, "1d_texture") == 0) {
2080  return TT_1d_texture;
2081  } else if (cmp_nocase(str, "2d_texture") == 0) {
2082  return TT_2d_texture;
2083  } else if (cmp_nocase(str, "3d_texture") == 0) {
2084  return TT_3d_texture;
2085  } else if (cmp_nocase(str, "2d_texture_array") == 0) {
2086  return TT_2d_texture_array;
2087  } else if (cmp_nocase(str, "cube_map") == 0) {
2088  return TT_cube_map;
2089  } else if (cmp_nocase(str, "cube_map_array") == 0) {
2090  return TT_cube_map_array;
2091  } else if (cmp_nocase(str, "buffer_texture") == 0) {
2092  return TT_buffer_texture;
2093  }
2094 
2095  gobj_cat->error()
2096  << "Invalid Texture::TextureType value: " << str << "\n";
2097  return TT_2d_texture;
2098 }
2099 
2100 /**
2101  * Returns the indicated ComponentType converted to a string word.
2102  */
2103 string Texture::
2104 format_component_type(ComponentType ct) {
2105  switch (ct) {
2106  case T_unsigned_byte:
2107  return "unsigned_byte";
2108  case T_unsigned_short:
2109  return "unsigned_short";
2110  case T_float:
2111  return "float";
2112  case T_unsigned_int_24_8:
2113  return "unsigned_int_24_8";
2114  case T_int:
2115  return "int";
2116  case T_byte:
2117  return "unsigned_byte";
2118  case T_short:
2119  return "short";
2120  case T_half_float:
2121  return "half_float";
2122  case T_unsigned_int:
2123  return "unsigned_int";
2124  }
2125 
2126  return "**invalid**";
2127 }
2128 
2129 /**
2130  * Returns the ComponentType corresponding to the indicated string word.
2131  */
2132 Texture::ComponentType Texture::
2133 string_component_type(const string &str) {
2134  if (cmp_nocase(str, "unsigned_byte") == 0) {
2135  return T_unsigned_byte;
2136  } else if (cmp_nocase(str, "unsigned_short") == 0) {
2137  return T_unsigned_short;
2138  } else if (cmp_nocase(str, "float") == 0) {
2139  return T_float;
2140  } else if (cmp_nocase(str, "unsigned_int_24_8") == 0) {
2141  return T_unsigned_int_24_8;
2142  } else if (cmp_nocase(str, "int") == 0) {
2143  return T_int;
2144  } else if (cmp_nocase(str, "byte") == 0) {
2145  return T_byte;
2146  } else if (cmp_nocase(str, "short") == 0) {
2147  return T_short;
2148  } else if (cmp_nocase(str, "half_float") == 0) {
2149  return T_half_float;
2150  } else if (cmp_nocase(str, "unsigned_int") == 0) {
2151  return T_unsigned_int;
2152  }
2153 
2154  gobj_cat->error()
2155  << "Invalid Texture::ComponentType value: " << str << "\n";
2156  return T_unsigned_byte;
2157 }
2158 
2159 /**
2160  * Returns the indicated Format converted to a string word.
2161  */
2162 string Texture::
2163 format_format(Format format) {
2164  switch (format) {
2165  case F_depth_stencil:
2166  return "depth_stencil";
2167  case F_depth_component:
2168  return "depth_component";
2169  case F_depth_component16:
2170  return "depth_component16";
2171  case F_depth_component24:
2172  return "depth_component24";
2173  case F_depth_component32:
2174  return "depth_component32";
2175  case F_color_index:
2176  return "color_index";
2177  case F_red:
2178  return "red";
2179  case F_green:
2180  return "green";
2181  case F_blue:
2182  return "blue";
2183  case F_alpha:
2184  return "alpha";
2185  case F_rgb:
2186  return "rgb";
2187  case F_rgb5:
2188  return "rgb5";
2189  case F_rgb8:
2190  return "rgb8";
2191  case F_rgb12:
2192  return "rgb12";
2193  case F_rgb332:
2194  return "rgb332";
2195  case F_rgba:
2196  return "rgba";
2197  case F_rgbm:
2198  return "rgbm";
2199  case F_rgba4:
2200  return "rgba4";
2201  case F_rgba5:
2202  return "rgba5";
2203  case F_rgba8:
2204  return "rgba8";
2205  case F_rgba12:
2206  return "rgba12";
2207  case F_luminance:
2208  return "luminance";
2209  case F_luminance_alpha:
2210  return "luminance_alpha";
2211  case F_luminance_alphamask:
2212  return "luminance_alphamask";
2213  case F_rgba16:
2214  return "rgba16";
2215  case F_rgba32:
2216  return "rgba32";
2217  case F_r16:
2218  return "r16";
2219  case F_r16i:
2220  return "r16i";
2221  case F_rg16:
2222  return "rg16";
2223  case F_rgb16:
2224  return "rgb16";
2225  case F_srgb:
2226  return "srgb";
2227  case F_srgb_alpha:
2228  return "srgb_alpha";
2229  case F_sluminance:
2230  return "sluminance";
2231  case F_sluminance_alpha:
2232  return "sluminance_alpha";
2233  case F_r32i:
2234  return "r32i";
2235  case F_r32:
2236  return "r32";
2237  case F_rg32:
2238  return "rg32";
2239  case F_rgb32:
2240  return "rgb32";
2241  case F_r8i:
2242  return "r8i";
2243  case F_rg8i:
2244  return "rg8i";
2245  case F_rgb8i:
2246  return "rgb8i";
2247  case F_rgba8i:
2248  return "rgba8i";
2249  case F_r11_g11_b10:
2250  return "r11g11b10";
2251  case F_rgb9_e5:
2252  return "rgb9_e5";
2253  case F_rgb10_a2:
2254  return "rgb10_a2";
2255  case F_rg:
2256  return "rg";
2257  }
2258  return "**invalid**";
2259 }
2260 
2261 /**
2262  * Returns the Format corresponding to the indicated string word.
2263  */
2264 Texture::Format Texture::
2265 string_format(const string &str) {
2266  if (cmp_nocase(str, "depth_stencil") == 0) {
2267  return F_depth_stencil;
2268  } else if (cmp_nocase(str, "depth_component") == 0) {
2269  return F_depth_component;
2270  } else if (cmp_nocase(str, "depth_component16") == 0 || cmp_nocase(str, "d16") == 0) {
2271  return F_depth_component16;
2272  } else if (cmp_nocase(str, "depth_component24") == 0 || cmp_nocase(str, "d24") == 0) {
2273  return F_depth_component24;
2274  } else if (cmp_nocase(str, "depth_component32") == 0 || cmp_nocase(str, "d32") == 0) {
2275  return F_depth_component32;
2276  } else if (cmp_nocase(str, "color_index") == 0) {
2277  return F_color_index;
2278  } else if (cmp_nocase(str, "red") == 0) {
2279  return F_red;
2280  } else if (cmp_nocase(str, "green") == 0) {
2281  return F_green;
2282  } else if (cmp_nocase(str, "blue") == 0) {
2283  return F_blue;
2284  } else if (cmp_nocase(str, "alpha") == 0) {
2285  return F_alpha;
2286  } else if (cmp_nocase(str, "rgb") == 0) {
2287  return F_rgb;
2288  } else if (cmp_nocase(str, "rgb5") == 0) {
2289  return F_rgb5;
2290  } else if (cmp_nocase(str, "rgb8") == 0 || cmp_nocase(str, "r8g8b8") == 0) {
2291  return F_rgb8;
2292  } else if (cmp_nocase(str, "rgb12") == 0) {
2293  return F_rgb12;
2294  } else if (cmp_nocase(str, "rgb332") == 0 || cmp_nocase(str, "r3g3b2") == 0) {
2295  return F_rgb332;
2296  } else if (cmp_nocase(str, "rgba") == 0) {
2297  return F_rgba;
2298  } else if (cmp_nocase(str, "rgbm") == 0) {
2299  return F_rgbm;
2300  } else if (cmp_nocase(str, "rgba4") == 0) {
2301  return F_rgba4;
2302  } else if (cmp_nocase(str, "rgba5") == 0) {
2303  return F_rgba5;
2304  } else if (cmp_nocase(str, "rgba8") == 0 || cmp_nocase(str, "r8g8b8a8") == 0) {
2305  return F_rgba8;
2306  } else if (cmp_nocase(str, "rgba12") == 0) {
2307  return F_rgba12;
2308  } else if (cmp_nocase(str, "luminance") == 0) {
2309  return F_luminance;
2310  } else if (cmp_nocase(str, "luminance_alpha") == 0) {
2311  return F_luminance_alpha;
2312  } else if (cmp_nocase(str, "luminance_alphamask") == 0) {
2313  return F_luminance_alphamask;
2314  } else if (cmp_nocase(str, "rgba16") == 0 || cmp_nocase(str, "r16g16b16a16") == 0) {
2315  return F_rgba16;
2316  } else if (cmp_nocase(str, "rgba32") == 0 || cmp_nocase(str, "r32g32b32a32") == 0) {
2317  return F_rgba32;
2318  } else if (cmp_nocase(str, "r16") == 0 || cmp_nocase(str, "red16") == 0) {
2319  return F_r16;
2320  } else if (cmp_nocase(str, "r16i") == 0) {
2321  return F_r16i;
2322  } else if (cmp_nocase(str, "rg16") == 0 || cmp_nocase(str, "r16g16") == 0) {
2323  return F_rg16;
2324  } else if (cmp_nocase(str, "rgb16") == 0 || cmp_nocase(str, "r16g16b16") == 0) {
2325  return F_rgb16;
2326  } else if (cmp_nocase(str, "srgb") == 0) {
2327  return F_srgb;
2328  } else if (cmp_nocase(str, "srgb_alpha") == 0) {
2329  return F_srgb_alpha;
2330  } else if (cmp_nocase(str, "sluminance") == 0) {
2331  return F_sluminance;
2332  } else if (cmp_nocase(str, "sluminance_alpha") == 0) {
2333  return F_sluminance_alpha;
2334  } else if (cmp_nocase(str, "r32i") == 0) {
2335  return F_r32i;
2336  } else if (cmp_nocase(str, "r32") == 0 || cmp_nocase(str, "red32") == 0) {
2337  return F_r32;
2338  } else if (cmp_nocase(str, "rg32") == 0 || cmp_nocase(str, "r32g32") == 0) {
2339  return F_rg32;
2340  } else if (cmp_nocase(str, "rgb32") == 0 || cmp_nocase(str, "r32g32b32") == 0) {
2341  return F_rgb32;
2342  } else if (cmp_nocase(str, "r11g11b10") == 0) {
2343  return F_r11_g11_b10;
2344  } else if (cmp_nocase(str, "rgb9_e5") == 0) {
2345  return F_rgb9_e5;
2346  } else if (cmp_nocase_uh(str, "rgb10_a2") == 0 || cmp_nocase(str, "r10g10b10a2") == 0) {
2347  return F_rgb10_a2;
2348  } else if (cmp_nocase_uh(str, "rg") == 0) {
2349  return F_rg;
2350  }
2351 
2352  gobj_cat->error()
2353  << "Invalid Texture::Format value: " << str << "\n";
2354  return F_rgba;
2355 }
2356 
2357 /**
2358  * Returns the indicated CompressionMode converted to a string word.
2359  */
2360 string Texture::
2361 format_compression_mode(CompressionMode cm) {
2362  switch (cm) {
2363  case CM_default:
2364  return "default";
2365  case CM_off:
2366  return "off";
2367  case CM_on:
2368  return "on";
2369  case CM_fxt1:
2370  return "fxt1";
2371  case CM_dxt1:
2372  return "dxt1";
2373  case CM_dxt2:
2374  return "dxt2";
2375  case CM_dxt3:
2376  return "dxt3";
2377  case CM_dxt4:
2378  return "dxt4";
2379  case CM_dxt5:
2380  return "dxt5";
2381  case CM_pvr1_2bpp:
2382  return "pvr1_2bpp";
2383  case CM_pvr1_4bpp:
2384  return "pvr1_4bpp";
2385  case CM_rgtc:
2386  return "rgtc";
2387  case CM_etc1:
2388  return "etc1";
2389  case CM_etc2:
2390  return "etc2";
2391  case CM_eac:
2392  return "eac";
2393  }
2394 
2395  return "**invalid**";
2396 }
2397 
2398 /**
2399  * Returns the CompressionMode value associated with the given string
2400  * representation.
2401  */
2402 Texture::CompressionMode Texture::
2403 string_compression_mode(const string &str) {
2404  if (cmp_nocase_uh(str, "default") == 0) {
2405  return CM_default;
2406  } else if (cmp_nocase_uh(str, "off") == 0) {
2407  return CM_off;
2408  } else if (cmp_nocase_uh(str, "on") == 0) {
2409  return CM_on;
2410  } else if (cmp_nocase_uh(str, "fxt1") == 0) {
2411  return CM_fxt1;
2412  } else if (cmp_nocase_uh(str, "dxt1") == 0) {
2413  return CM_dxt1;
2414  } else if (cmp_nocase_uh(str, "dxt2") == 0) {
2415  return CM_dxt2;
2416  } else if (cmp_nocase_uh(str, "dxt3") == 0) {
2417  return CM_dxt3;
2418  } else if (cmp_nocase_uh(str, "dxt4") == 0) {
2419  return CM_dxt4;
2420  } else if (cmp_nocase_uh(str, "dxt5") == 0) {
2421  return CM_dxt5;
2422  } else if (cmp_nocase_uh(str, "pvr1_2bpp") == 0) {
2423  return CM_pvr1_2bpp;
2424  } else if (cmp_nocase_uh(str, "pvr1_4bpp") == 0) {
2425  return CM_pvr1_4bpp;
2426  } else if (cmp_nocase_uh(str, "rgtc") == 0) {
2427  return CM_rgtc;
2428  } else if (cmp_nocase_uh(str, "etc1") == 0) {
2429  return CM_etc1;
2430  } else if (cmp_nocase_uh(str, "etc2") == 0) {
2431  return CM_etc2;
2432  } else if (cmp_nocase_uh(str, "eac") == 0) {
2433  return CM_eac;
2434  }
2435 
2436  gobj_cat->error()
2437  << "Invalid Texture::CompressionMode value: " << str << "\n";
2438  return CM_default;
2439 }
2440 
2441 
2442 /**
2443  * Returns the indicated QualityLevel converted to a string word.
2444  */
2445 string Texture::
2446 format_quality_level(QualityLevel ql) {
2447  switch (ql) {
2448  case QL_default:
2449  return "default";
2450  case QL_fastest:
2451  return "fastest";
2452  case QL_normal:
2453  return "normal";
2454  case QL_best:
2455  return "best";
2456  }
2457 
2458  return "**invalid**";
2459 }
2460 
2461 /**
2462  * Returns the QualityLevel value associated with the given string
2463  * representation.
2464  */
2465 Texture::QualityLevel Texture::
2466 string_quality_level(const string &str) {
2467  if (cmp_nocase(str, "default") == 0) {
2468  return QL_default;
2469  } else if (cmp_nocase(str, "fastest") == 0) {
2470  return QL_fastest;
2471  } else if (cmp_nocase(str, "normal") == 0) {
2472  return QL_normal;
2473  } else if (cmp_nocase(str, "best") == 0) {
2474  return QL_best;
2475  }
2476 
2477  gobj_cat->error()
2478  << "Invalid Texture::QualityLevel value: " << str << "\n";
2479  return QL_default;
2480 }
2481 
2482 /**
2483  * This method is called by the GraphicsEngine at the beginning of the frame
2484  * *after* a texture has been successfully uploaded to graphics memory. It is
2485  * intended as a callback so the texture can release its RAM image, if
2486  * _keep_ram_image is false.
2487  *
2488  * This is called indirectly when the GSG calls
2489  * GraphicsEngine::texture_uploaded().
2490  */
2491 void Texture::
2493  CDLockedReader cdata(_cycler);
2494 
2495  if (!keep_texture_ram && !cdata->_keep_ram_image) {
2496  // Once we have prepared the texture, we can generally safely remove the
2497  // pixels from main RAM. The GSG is now responsible for remembering what
2498  // it looks like.
2499 
2500  CDWriter cdataw(_cycler, cdata, false);
2501  if (gobj_cat.is_debug()) {
2502  gobj_cat.debug()
2503  << "Dumping RAM for texture " << get_name() << "\n";
2504  }
2505  do_clear_ram_image(cdataw);
2506  }
2507 }
2508 
2509 /**
2510  * Should be overridden by derived classes to return true if cull_callback()
2511  * has been defined. Otherwise, returns false to indicate cull_callback()
2512  * does not need to be called for this node during the cull traversal.
2513  */
2514 bool Texture::
2516  return false;
2517 }
2518 
2519 /**
2520  * If has_cull_callback() returns true, this function will be called during
2521  * the cull traversal to perform any additional operations that should be
2522  * performed at cull time.
2523  *
2524  * This is called each time the Texture is discovered applied to a Geom in the
2525  * traversal. It should return true if the Geom is visible, false if it
2526  * should be omitted.
2527  */
2528 bool Texture::
2530  return true;
2531 }
2532 
2533 /**
2534  * A factory function to make a new Texture, used to pass to the TexturePool.
2535  */
2536 PT(Texture) Texture::
2537 make_texture() {
2538  return new Texture;
2539 }
2540 
2541 /**
2542  * Returns true if the indicated component type is unsigned, false otherwise.
2543  */
2544 bool Texture::
2545 is_unsigned(Texture::ComponentType ctype) {
2546  return (ctype == T_unsigned_byte ||
2547  ctype == T_unsigned_short ||
2548  ctype == T_unsigned_int_24_8 ||
2549  ctype == T_unsigned_int);
2550 }
2551 
2552 /**
2553  * Returns true if the indicated compression mode is one of the specific
2554  * compression types, false otherwise.
2555  */
2556 bool Texture::
2557 is_specific(Texture::CompressionMode compression) {
2558  switch (compression) {
2559  case CM_default:
2560  case CM_off:
2561  case CM_on:
2562  return false;
2563 
2564  default:
2565  return true;
2566  }
2567 }
2568 
2569 /**
2570  * Returns true if the indicated format includes alpha, false otherwise.
2571  */
2572 bool Texture::
2573 has_alpha(Format format) {
2574  switch (format) {
2575  case F_alpha:
2576  case F_rgba:
2577  case F_rgbm:
2578  case F_rgba4:
2579  case F_rgba5:
2580  case F_rgba8:
2581  case F_rgba12:
2582  case F_rgba16:
2583  case F_rgba32:
2584  case F_luminance_alpha:
2585  case F_luminance_alphamask:
2586  case F_srgb_alpha:
2587  case F_sluminance_alpha:
2588  case F_rgba8i:
2589  case F_rgb10_a2:
2590  return true;
2591 
2592  default:
2593  return false;
2594  }
2595 }
2596 
2597 /**
2598  * Returns true if the indicated format includes a binary alpha only, false
2599  * otherwise.
2600  */
2601 bool Texture::
2602 has_binary_alpha(Format format) {
2603  switch (format) {
2604  case F_rgbm:
2605  return true;
2606 
2607  default:
2608  return false;
2609  }
2610 }
2611 
2612 /**
2613  * Returns true if the indicated format is in the sRGB color space, false
2614  * otherwise.
2615  */
2616 bool Texture::
2617 is_srgb(Format format) {
2618  switch (format) {
2619  case F_srgb:
2620  case F_srgb_alpha:
2621  case F_sluminance:
2622  case F_sluminance_alpha:
2623  return true;
2624 
2625  default:
2626  return false;
2627  }
2628 }
2629 
2630 /**
2631  * Computes the proper size of the texture, based on the original size, the
2632  * filename, and the resizing whims of the config file.
2633  *
2634  * x_size and y_size should be loaded with the texture image's original size
2635  * on disk. On return, they will be loaded with the texture's in-memory
2636  * target size. The return value is true if the size has been adjusted, or
2637  * false if it is the same.
2638  */
2639 bool Texture::
2640 adjust_size(int &x_size, int &y_size, const string &name,
2641  bool for_padding, AutoTextureScale auto_texture_scale) {
2642  bool exclude = false;
2643  int num_excludes = exclude_texture_scale.get_num_unique_values();
2644  for (int i = 0; i < num_excludes && !exclude; ++i) {
2645  GlobPattern pat(exclude_texture_scale.get_unique_value(i));
2646  if (pat.matches(name)) {
2647  exclude = true;
2648  }
2649  }
2650 
2651  int new_x_size = x_size;
2652  int new_y_size = y_size;
2653 
2654  if (!exclude) {
2655  new_x_size = (int)cfloor(new_x_size * texture_scale + 0.5);
2656  new_y_size = (int)cfloor(new_y_size * texture_scale + 0.5);
2657 
2658  // Don't auto-scale below 4 in either dimension. This causes problems for
2659  // DirectX and texture compression.
2660  new_x_size = min(max(new_x_size, (int)texture_scale_limit), x_size);
2661  new_y_size = min(max(new_y_size, (int)texture_scale_limit), y_size);
2662  }
2663 
2664  AutoTextureScale ats = auto_texture_scale;
2665  if (ats == ATS_unspecified) {
2666  ats = get_textures_power_2();
2667  }
2668  if (!for_padding && ats == ATS_pad) {
2669  // If we're not calculating the padding size--that is, we're calculating
2670  // the initial scaling size instead--then ignore ATS_pad, and treat it the
2671  // same as ATS_none.
2672  ats = ATS_none;
2673  }
2674 
2675  switch (ats) {
2676  case ATS_down:
2677  new_x_size = down_to_power_2(new_x_size);
2678  new_y_size = down_to_power_2(new_y_size);
2679  break;
2680 
2681  case ATS_up:
2682  case ATS_pad:
2683  new_x_size = up_to_power_2(new_x_size);
2684  new_y_size = up_to_power_2(new_y_size);
2685  break;
2686 
2687  case ATS_none:
2688  case ATS_unspecified:
2689  break;
2690  }
2691 
2692  ats = textures_square.get_value();
2693  if (!for_padding && ats == ATS_pad) {
2694  ats = ATS_none;
2695  }
2696  switch (ats) {
2697  case ATS_down:
2698  new_x_size = new_y_size = min(new_x_size, new_y_size);
2699  break;
2700 
2701  case ATS_up:
2702  case ATS_pad:
2703  new_x_size = new_y_size = max(new_x_size, new_y_size);
2704  break;
2705 
2706  case ATS_none:
2707  case ATS_unspecified:
2708  break;
2709  }
2710 
2711  if (!exclude) {
2712  int max_dimension = max_texture_dimension;
2713 
2714  if (max_dimension < 0) {
2716  if (gsg != nullptr) {
2717  max_dimension = gsg->get_max_texture_dimension();
2718  }
2719  }
2720 
2721  if (max_dimension > 0) {
2722  new_x_size = min(new_x_size, (int)max_dimension);
2723  new_y_size = min(new_y_size, (int)max_dimension);
2724  }
2725  }
2726 
2727  if (x_size != new_x_size || y_size != new_y_size) {
2728  x_size = new_x_size;
2729  y_size = new_y_size;
2730  return true;
2731  }
2732 
2733  return false;
2734 }
2735 
2736 /**
2737  * May be called prior to calling read_txo() or any bam-related Texture-
2738  * creating callback, to ensure that the proper dynamic libraries for a
2739  * Texture of the current class type, and the indicated filename, have been
2740  * already loaded.
2741  *
2742  * This is a low-level function that should not normally need to be called
2743  * directly by the user.
2744  *
2745  * Note that for best results you must first create a Texture object of the
2746  * appropriate class type for your filename, for instance with
2747  * TexturePool::make_texture().
2748  */
2749 void Texture::
2750 ensure_loader_type(const Filename &filename) {
2751  // For a plain Texture type, this doesn't need to do anything.
2752 }
2753 
2754 /**
2755  * Called by TextureContext to give the Texture a chance to mark itself dirty
2756  * before rendering, if necessary.
2757  */
2758 void Texture::
2759 reconsider_dirty() {
2760 }
2761 
2762 /**
2763  * Works like adjust_size, but also considers the texture class. Movie
2764  * textures, for instance, always pad outwards, regardless of textures-
2765  * power-2.
2766  */
2767 bool Texture::
2768 do_adjust_this_size(const CData *cdata, int &x_size, int &y_size, const string &name,
2769  bool for_padding) const {
2770  return adjust_size(x_size, y_size, name, for_padding, cdata->_auto_texture_scale);
2771 }
2772 
2773 /**
2774  * The internal implementation of the various read() methods.
2775  */
2776 bool Texture::
2777 do_read(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpath,
2778  int primary_file_num_channels, int alpha_file_channel,
2779  int z, int n, bool read_pages, bool read_mipmaps,
2780  const LoaderOptions &options, BamCacheRecord *record) {
2781  PStatTimer timer(_texture_read_pcollector);
2782 
2783  if (options.get_auto_texture_scale() != ATS_unspecified) {
2784  cdata->_auto_texture_scale = options.get_auto_texture_scale();
2785  }
2786 
2787  bool header_only = ((options.get_texture_flags() & (LoaderOptions::TF_preload | LoaderOptions::TF_preload_simple)) == 0);
2788  if (record != nullptr) {
2789  header_only = false;
2790  }
2791 
2792  if ((z == 0 || read_pages) && (n == 0 || read_mipmaps)) {
2793  // When we re-read the page 0 of the base image, we clear everything and
2794  // start over.
2795  do_clear_ram_image(cdata);
2796  }
2797 
2798  if (is_txo_filename(fullpath)) {
2799  if (record != nullptr) {
2800  record->add_dependent_file(fullpath);
2801  }
2802  return do_read_txo_file(cdata, fullpath);
2803  }
2804 
2805  if (is_dds_filename(fullpath)) {
2806  if (record != nullptr) {
2807  record->add_dependent_file(fullpath);
2808  }
2809  return do_read_dds_file(cdata, fullpath, header_only);
2810  }
2811 
2812  if (is_ktx_filename(fullpath)) {
2813  if (record != nullptr) {
2814  record->add_dependent_file(fullpath);
2815  }
2816  return do_read_ktx_file(cdata, fullpath, header_only);
2817  }
2818 
2819  // If read_pages or read_mipmaps is specified, then z and n actually
2820  // indicate z_size and n_size, respectively--the numerical limits on which
2821  // to search for filenames.
2822  int z_size = z;
2823  int n_size = n;
2824 
2825  // Certain texture types have an implicit z_size. If z_size is omitted,
2826  // choose an appropriate default based on the texture type.
2827  if (z_size == 0) {
2828  switch (cdata->_texture_type) {
2829  case TT_1d_texture:
2830  case TT_2d_texture:
2831  case TT_buffer_texture:
2832  z_size = 1;
2833  break;
2834 
2835  case TT_cube_map:
2836  z_size = 6;
2837  break;
2838 
2839  default:
2840  break;
2841  }
2842  }
2843 
2844  int num_views = 0;
2845  if (options.get_texture_flags() & LoaderOptions::TF_multiview) {
2846  // We'll be loading a multiview texture.
2847  read_pages = true;
2848  if (options.get_texture_num_views() != 0) {
2849  num_views = options.get_texture_num_views();
2850  do_set_num_views(cdata, num_views);
2851  }
2852  }
2853 
2855 
2856  if (read_pages && read_mipmaps) {
2857  // Read a sequence of pages * mipmap levels.
2858  Filename fullpath_pattern = Filename::pattern_filename(fullpath);
2859  Filename alpha_fullpath_pattern = Filename::pattern_filename(alpha_fullpath);
2860  do_set_z_size(cdata, z_size);
2861 
2862  n = 0;
2863  while (true) {
2864  // For mipmap level 0, the total number of pages might be determined by
2865  // the number of files we find. After mipmap level 0, though, the
2866  // number of pages is predetermined.
2867  if (n != 0) {
2868  z_size = do_get_expected_mipmap_z_size(cdata, n);
2869  }
2870 
2871  z = 0;
2872 
2873  Filename n_pattern = Filename::pattern_filename(fullpath_pattern.get_filename_index(z));
2874  Filename alpha_n_pattern = Filename::pattern_filename(alpha_fullpath_pattern.get_filename_index(z));
2875 
2876  if (!n_pattern.has_hash()) {
2877  gobj_cat.error()
2878  << "Filename requires two different hash sequences: " << fullpath
2879  << "\n";
2880  return false;
2881  }
2882 
2883  Filename file = n_pattern.get_filename_index(n);
2884  Filename alpha_file = alpha_n_pattern.get_filename_index(n);
2885 
2886  if ((n_size == 0 && (vfs->exists(file) || n == 0)) ||
2887  (n_size != 0 && n < n_size)) {
2888  // Continue through the loop.
2889  } else {
2890  // We've reached the end of the mipmap sequence.
2891  break;
2892  }
2893 
2894  int num_pages = z_size * num_views;
2895  while ((num_pages == 0 && (vfs->exists(file) || z == 0)) ||
2896  (num_pages != 0 && z < num_pages)) {
2897  if (!do_read_one(cdata, file, alpha_file, z, n, primary_file_num_channels,
2898  alpha_file_channel, options, header_only, record)) {
2899  return false;
2900  }
2901  ++z;
2902 
2903  n_pattern = Filename::pattern_filename(fullpath_pattern.get_filename_index(z));
2904  file = n_pattern.get_filename_index(n);
2905  alpha_file = alpha_n_pattern.get_filename_index(n);
2906  }
2907 
2908  if (n == 0 && n_size == 0) {
2909  // If n_size is not specified, it gets implicitly set after we read
2910  // the base texture image (which determines the size of the texture).
2911  n_size = do_get_expected_num_mipmap_levels(cdata);
2912  }
2913  ++n;
2914  }
2915  cdata->_fullpath = fullpath_pattern;
2916  cdata->_alpha_fullpath = alpha_fullpath_pattern;
2917 
2918  } else if (read_pages) {
2919  // Read a sequence of cube map or 3-D texture pages.
2920  Filename fullpath_pattern = Filename::pattern_filename(fullpath);
2921  Filename alpha_fullpath_pattern = Filename::pattern_filename(alpha_fullpath);
2922  if (!fullpath_pattern.has_hash()) {
2923  gobj_cat.error()
2924  << "Filename requires a hash mark: " << fullpath
2925  << "\n";
2926  return false;
2927  }
2928 
2929  do_set_z_size(cdata, z_size);
2930  z = 0;
2931  Filename file = fullpath_pattern.get_filename_index(z);
2932  Filename alpha_file = alpha_fullpath_pattern.get_filename_index(z);
2933 
2934  int num_pages = z_size * num_views;
2935  while ((num_pages == 0 && (vfs->exists(file) || z == 0)) ||
2936  (num_pages != 0 && z < num_pages)) {
2937  if (!do_read_one(cdata, file, alpha_file, z, 0, primary_file_num_channels,
2938  alpha_file_channel, options, header_only, record)) {
2939  return false;
2940  }
2941  ++z;
2942 
2943  file = fullpath_pattern.get_filename_index(z);
2944  alpha_file = alpha_fullpath_pattern.get_filename_index(z);
2945  }
2946  cdata->_fullpath = fullpath_pattern;
2947  cdata->_alpha_fullpath = alpha_fullpath_pattern;
2948 
2949  } else if (read_mipmaps) {
2950  // Read a sequence of mipmap levels.
2951  Filename fullpath_pattern = Filename::pattern_filename(fullpath);
2952  Filename alpha_fullpath_pattern = Filename::pattern_filename(alpha_fullpath);
2953  if (!fullpath_pattern.has_hash()) {
2954  gobj_cat.error()
2955  << "Filename requires a hash mark: " << fullpath
2956  << "\n";
2957  return false;
2958  }
2959 
2960  n = 0;
2961  Filename file = fullpath_pattern.get_filename_index(n);
2962  Filename alpha_file = alpha_fullpath_pattern.get_filename_index(n);
2963 
2964  while ((n_size == 0 && (vfs->exists(file) || n == 0)) ||
2965  (n_size != 0 && n < n_size)) {
2966  if (!do_read_one(cdata, file, alpha_file, z, n,
2967  primary_file_num_channels, alpha_file_channel,
2968  options, header_only, record)) {
2969  return false;
2970  }
2971  ++n;
2972 
2973  if (n_size == 0 && n >= do_get_expected_num_mipmap_levels(cdata)) {
2974  // Don't try to read more than the requisite number of mipmap levels
2975  // (unless the user insisted on it for some reason).
2976  break;
2977  }
2978 
2979  file = fullpath_pattern.get_filename_index(n);
2980  alpha_file = alpha_fullpath_pattern.get_filename_index(n);
2981  }
2982  cdata->_fullpath = fullpath_pattern;
2983  cdata->_alpha_fullpath = alpha_fullpath_pattern;
2984 
2985  } else {
2986  // Just an ordinary read of one file.
2987  if (!do_read_one(cdata, fullpath, alpha_fullpath, z, n,
2988  primary_file_num_channels, alpha_file_channel,
2989  options, header_only, record)) {
2990  return false;
2991  }
2992  }
2993 
2994  cdata->_has_read_pages = read_pages;
2995  cdata->_has_read_mipmaps = read_mipmaps;
2996  cdata->_num_mipmap_levels_read = cdata->_ram_images.size();
2997 
2998  if (header_only) {
2999  // If we were only supposed to be checking the image header information,
3000  // don't let the Texture think that it's got the image now.
3001  do_clear_ram_image(cdata);
3002  } else {
3003  if ((options.get_texture_flags() & LoaderOptions::TF_preload) != 0) {
3004  // If we intend to keep the ram image around, consider compressing it
3005  // etc.
3006  bool generate_mipmaps = ((options.get_texture_flags() & LoaderOptions::TF_generate_mipmaps) != 0);
3007  bool allow_compression = ((options.get_texture_flags() & LoaderOptions::TF_allow_compression) != 0);
3008  do_consider_auto_process_ram_image(cdata, generate_mipmaps || uses_mipmaps(), allow_compression);
3009  }
3010  }
3011 
3012  return true;
3013 }
3014 
3015 /**
3016  * Called only from do_read(), this method reads a single image file, either
3017  * one page or one mipmap level.
3018  */
3019 bool Texture::
3020 do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpath,
3021  int z, int n, int primary_file_num_channels, int alpha_file_channel,
3022  const LoaderOptions &options, bool header_only, BamCacheRecord *record) {
3023  if (record != nullptr) {
3024  nassertr(!header_only, false);
3025  record->add_dependent_file(fullpath);
3026  }
3027 
3028  PNMImage image;
3029  PfmFile pfm;
3030  PNMReader *image_reader = image.make_reader(fullpath, nullptr, false);
3031  if (image_reader == nullptr) {
3032  gobj_cat.error()
3033  << "Texture::read() - couldn't read: " << fullpath << endl;
3034  return false;
3035  }
3036  image.copy_header_from(*image_reader);
3037 
3038  AutoTextureScale auto_texture_scale = do_get_auto_texture_scale(cdata);
3039 
3040  // If it's a floating-point image file, read it by default into a floating-
3041  // point texture.
3042  bool read_floating_point;
3043  int texture_load_type = (options.get_texture_flags() & (LoaderOptions::TF_integer | LoaderOptions::TF_float));
3044  switch (texture_load_type) {
3045  case LoaderOptions::TF_integer:
3046  read_floating_point = false;
3047  break;
3048 
3049  case LoaderOptions::TF_float:
3050  read_floating_point = true;
3051  break;
3052 
3053  default:
3054  // Neither TF_integer nor TF_float was specified; determine which way the
3055  // texture wants to be loaded.
3056  read_floating_point = (image_reader->is_floating_point());
3057  if (!alpha_fullpath.empty()) {
3058  read_floating_point = false;
3059  }
3060  }
3061 
3062  if (header_only || textures_header_only) {
3063  int x_size = image.get_x_size();
3064  int y_size = image.get_y_size();
3065  if (z == 0 && n == 0) {
3066  cdata->_orig_file_x_size = x_size;
3067  cdata->_orig_file_y_size = y_size;
3068  }
3069 
3070  if (textures_header_only) {
3071  // In this mode, we never intend to load the actual texture image
3072  // anyway, so we don't even need to make the size right.
3073  x_size = 1;
3074  y_size = 1;
3075 
3076  } else {
3077  adjust_size(x_size, y_size, fullpath.get_basename(), false, auto_texture_scale);
3078  }
3079 
3080  if (read_floating_point) {
3081  pfm.clear(x_size, y_size, image.get_num_channels());
3082  } else {
3083  image = PNMImage(x_size, y_size, image.get_num_channels(),
3084  image.get_maxval(), image.get_type(),
3085  image.get_color_space());
3086  image.fill(0.2, 0.3, 1.0);
3087  if (image.has_alpha()) {
3088  image.alpha_fill(1.0);
3089  }
3090  }
3091  delete image_reader;
3092 
3093  } else {
3094  if (z == 0 && n == 0) {
3095  int x_size = image.get_x_size();
3096  int y_size = image.get_y_size();
3097 
3098  cdata->_orig_file_x_size = x_size;
3099  cdata->_orig_file_y_size = y_size;
3100 
3101  if (adjust_size(x_size, y_size, fullpath.get_basename(), false, auto_texture_scale)) {
3102  image.set_read_size(x_size, y_size);
3103  }
3104  } else {
3105  image.set_read_size(do_get_expected_mipmap_x_size(cdata, n),
3106  do_get_expected_mipmap_y_size(cdata, n));
3107  }
3108 
3109  if (image.get_x_size() != image.get_read_x_size() ||
3110  image.get_y_size() != image.get_read_y_size()) {
3111  gobj_cat.info()
3112  << "Implicitly rescaling " << fullpath.get_basename() << " from "
3113  << image.get_x_size() << " by " << image.get_y_size() << " to "
3114  << image.get_read_x_size() << " by " << image.get_read_y_size()
3115  << "\n";
3116  }
3117 
3118  bool success;
3119  if (read_floating_point) {
3120  success = pfm.read(image_reader);
3121  } else {
3122  success = image.read(image_reader);
3123  }
3124 
3125  if (!success) {
3126  gobj_cat.error()
3127  << "Texture::read() - couldn't read: " << fullpath << endl;
3128  return false;
3129  }
3131  }
3132 
3133  PNMImage alpha_image;
3134  if (!alpha_fullpath.empty()) {
3135  PNMReader *alpha_image_reader = alpha_image.make_reader(alpha_fullpath, nullptr, false);
3136  if (alpha_image_reader == nullptr) {
3137  gobj_cat.error()
3138  << "Texture::read() - couldn't read: " << alpha_fullpath << endl;
3139  return false;
3140  }
3141  alpha_image.copy_header_from(*alpha_image_reader);
3142 
3143  if (record != nullptr) {
3144  record->add_dependent_file(alpha_fullpath);
3145  }
3146 
3147  if (header_only || textures_header_only) {
3148  int x_size = image.get_x_size();
3149  int y_size = image.get_y_size();
3150  alpha_image = PNMImage(x_size, y_size, alpha_image.get_num_channels(),
3151  alpha_image.get_maxval(), alpha_image.get_type(),
3152  alpha_image.get_color_space());
3153  alpha_image.fill(1.0);
3154  if (alpha_image.has_alpha()) {
3155  alpha_image.alpha_fill(1.0);
3156  }
3157  delete alpha_image_reader;
3158 
3159  } else {
3160  if (image.get_x_size() != alpha_image.get_x_size() ||
3161  image.get_y_size() != alpha_image.get_y_size()) {
3162  gobj_cat.info()
3163  << "Implicitly rescaling " << alpha_fullpath.get_basename()
3164  << " from " << alpha_image.get_x_size() << " by "
3165  << alpha_image.get_y_size() << " to " << image.get_x_size()
3166  << " by " << image.get_y_size() << "\n";
3167  alpha_image.set_read_size(image.get_x_size(), image.get_y_size());
3168  }
3169 
3170  if (!alpha_image.read(alpha_image_reader)) {
3171  gobj_cat.error()
3172  << "Texture::read() - couldn't read (alpha): " << alpha_fullpath << endl;
3173  return false;
3174  }
3176  }
3177  }
3178 
3179  if (z == 0 && n == 0) {
3180  if (!has_name()) {
3181  set_name(fullpath.get_basename_wo_extension());
3182  }
3183  if (cdata->_filename.empty()) {
3184  cdata->_filename = fullpath;
3185  cdata->_alpha_filename = alpha_fullpath;
3186 
3187  // The first time we set the filename via a read() operation, we clear
3188  // keep_ram_image. The user can always set it again later if he needs
3189  // to.
3190  cdata->_keep_ram_image = false;
3191  }
3192 
3193  cdata->_fullpath = fullpath;
3194  cdata->_alpha_fullpath = alpha_fullpath;
3195  }
3196 
3197  if (!alpha_fullpath.empty()) {
3198  // The grayscale (alpha channel) image must be the same size as the main
3199  // image. This should really have been already guaranteed by the above.
3200  if (image.get_x_size() != alpha_image.get_x_size() ||
3201  image.get_y_size() != alpha_image.get_y_size()) {
3202  gobj_cat.info()
3203  << "Automatically rescaling " << alpha_fullpath.get_basename()
3204  << " from " << alpha_image.get_x_size() << " by "
3205  << alpha_image.get_y_size() << " to " << image.get_x_size()
3206  << " by " << image.get_y_size() << "\n";
3207 
3208  PNMImage scaled(image.get_x_size(), image.get_y_size(),
3209  alpha_image.get_num_channels(),
3210  alpha_image.get_maxval(), alpha_image.get_type(),
3211  alpha_image.get_color_space());
3212  scaled.quick_filter_from(alpha_image);
3214  alpha_image = scaled;
3215  }
3216  }
3217 
3218  if (n == 0) {
3219  consider_downgrade(image, primary_file_num_channels, get_name());
3220  cdata->_primary_file_num_channels = image.get_num_channels();
3221  cdata->_alpha_file_channel = 0;
3222  }
3223 
3224  if (!alpha_fullpath.empty()) {
3225  // Make the original image a 4-component image by taking the grayscale
3226  // value from the second image.
3227  image.add_alpha();
3228 
3229  if (alpha_file_channel == 4 ||
3230  (alpha_file_channel == 2 && alpha_image.get_num_channels() == 2)) {
3231 
3232  if (!alpha_image.has_alpha()) {
3233  gobj_cat.error()
3234  << alpha_fullpath.get_basename() << " has no channel " << alpha_file_channel << ".\n";
3235  } else {
3236  // Use the alpha channel.
3237  for (int x = 0; x < image.get_x_size(); x++) {
3238  for (int y = 0; y < image.get_y_size(); y++) {
3239  image.set_alpha(x, y, alpha_image.get_alpha(x, y));
3240  }
3241  }
3242  }
3243  cdata->_alpha_file_channel = alpha_image.get_num_channels();
3244 
3245  } else if (alpha_file_channel >= 1 && alpha_file_channel <= 3 &&
3246  alpha_image.get_num_channels() >= 3) {
3247  // Use the appropriate red, green, or blue channel.
3248  for (int x = 0; x < image.get_x_size(); x++) {
3249  for (int y = 0; y < image.get_y_size(); y++) {
3250  image.set_alpha(x, y, alpha_image.get_channel_val(x, y, alpha_file_channel - 1));
3251  }
3252  }
3253  cdata->_alpha_file_channel = alpha_file_channel;
3254 
3255  } else {
3256  // Use the grayscale channel.
3257  for (int x = 0; x < image.get_x_size(); x++) {
3258  for (int y = 0; y < image.get_y_size(); y++) {
3259  image.set_alpha(x, y, alpha_image.get_gray(x, y));
3260  }
3261  }
3262  cdata->_alpha_file_channel = 0;
3263  }
3264  }
3265 
3266  if (read_floating_point) {
3267  if (!do_load_one(cdata, pfm, fullpath.get_basename(), z, n, options)) {
3268  return false;
3269  }
3270  } else {
3271  // Now see if we want to pad the image within a larger power-of-2 image.
3272  int pad_x_size = 0;
3273  int pad_y_size = 0;
3274  if (do_get_auto_texture_scale(cdata) == ATS_pad) {
3275  int new_x_size = image.get_x_size();
3276  int new_y_size = image.get_y_size();
3277  if (do_adjust_this_size(cdata, new_x_size, new_y_size, fullpath.get_basename(), true)) {
3278  pad_x_size = new_x_size - image.get_x_size();
3279  pad_y_size = new_y_size - image.get_y_size();
3280  PNMImage new_image(new_x_size, new_y_size, image.get_num_channels(),
3281  image.get_maxval(), image.get_type(),
3282  image.get_color_space());
3283  new_image.copy_sub_image(image, 0, new_y_size - image.get_y_size());
3284  image.take_from(new_image);
3285  }
3286  }
3287 
3288  if (!do_load_one(cdata, image, fullpath.get_basename(), z, n, options)) {
3289  return false;
3290  }
3291 
3292  do_set_pad_size(cdata, pad_x_size, pad_y_size, 0);
3293  }
3294  return true;
3295 }
3296 
3297 /**
3298  * Internal method to load a single page or mipmap level.
3299  */
3300 bool Texture::
3301 do_load_one(CData *cdata, const PNMImage &pnmimage, const string &name, int z, int n,
3302  const LoaderOptions &options) {
3303  if (cdata->_ram_images.size() <= 1 && n == 0) {
3304  // A special case for mipmap level 0. When we load mipmap level 0, unless
3305  // we already have mipmap levels, it determines the image properties like
3306  // size and number of components.
3307  if (!do_reconsider_z_size(cdata, z, options)) {
3308  return false;
3309  }
3310  nassertr(z >= 0 && z < cdata->_z_size * cdata->_num_views, false);
3311 
3312  if (z == 0) {
3313  ComponentType component_type = T_unsigned_byte;
3314  xelval maxval = pnmimage.get_maxval();
3315  if (maxval > 255) {
3316  component_type = T_unsigned_short;
3317  }
3318 
3319  if (!do_reconsider_image_properties(cdata, pnmimage.get_x_size(), pnmimage.get_y_size(),
3320  pnmimage.get_num_channels(), component_type,
3321  z, options)) {
3322  return false;
3323  }
3324  }
3325 
3326  do_modify_ram_image(cdata);
3327  cdata->_loaded_from_image = true;
3328  }
3329 
3330  do_modify_ram_mipmap_image(cdata, n);
3331 
3332  // Ensure the PNMImage is an appropriate size.
3333  int x_size = do_get_expected_mipmap_x_size(cdata, n);
3334  int y_size = do_get_expected_mipmap_y_size(cdata, n);
3335  if (pnmimage.get_x_size() != x_size ||
3336  pnmimage.get_y_size() != y_size) {
3337  gobj_cat.info()
3338  << "Automatically rescaling " << name;
3339  if (n != 0) {
3340  gobj_cat.info(false)
3341  << " mipmap level " << n;
3342  }
3343  gobj_cat.info(false)
3344  << " from " << pnmimage.get_x_size() << " by "
3345  << pnmimage.get_y_size() << " to " << x_size << " by "
3346  << y_size << "\n";
3347 
3348  PNMImage scaled(x_size, y_size, pnmimage.get_num_channels(),
3349  pnmimage.get_maxval(), pnmimage.get_type(),
3350  pnmimage.get_color_space());
3351  scaled.quick_filter_from(pnmimage);
3353 
3354  convert_from_pnmimage(cdata->_ram_images[n]._image,
3355  do_get_expected_ram_mipmap_page_size(cdata, n),
3356  x_size, 0, 0, z, scaled,
3357  cdata->_num_components, cdata->_component_width);
3358  } else {
3359  // Now copy the pixel data from the PNMImage into our internal
3360  // cdata->_image component.
3361  convert_from_pnmimage(cdata->_ram_images[n]._image,
3362  do_get_expected_ram_mipmap_page_size(cdata, n),
3363  x_size, 0, 0, z, pnmimage,
3364  cdata->_num_components, cdata->_component_width);
3365  }
3367 
3368  return true;
3369 }
3370 
3371 /**
3372  * Internal method to load a single page or mipmap level.
3373  */
3374 bool Texture::
3375 do_load_one(CData *cdata, const PfmFile &pfm, const string &name, int z, int n,
3376  const LoaderOptions &options) {
3377  if (cdata->_ram_images.size() <= 1 && n == 0) {
3378  // A special case for mipmap level 0. When we load mipmap level 0, unless
3379  // we already have mipmap levels, it determines the image properties like
3380  // size and number of components.
3381  if (!do_reconsider_z_size(cdata, z, options)) {
3382  return false;
3383  }
3384  nassertr(z >= 0 && z < cdata->_z_size * cdata->_num_views, false);
3385 
3386  if (z == 0) {
3387  ComponentType component_type = T_float;
3388  if (!do_reconsider_image_properties(cdata, pfm.get_x_size(), pfm.get_y_size(),
3389  pfm.get_num_channels(), component_type,
3390  z, options)) {
3391  return false;
3392  }
3393  }
3394 
3395  do_modify_ram_image(cdata);
3396  cdata->_loaded_from_image = true;
3397  }
3398 
3399  do_modify_ram_mipmap_image(cdata, n);
3400 
3401  // Ensure the PfmFile is an appropriate size.
3402  int x_size = do_get_expected_mipmap_x_size(cdata, n);
3403  int y_size = do_get_expected_mipmap_y_size(cdata, n);
3404  if (pfm.get_x_size() != x_size ||
3405  pfm.get_y_size() != y_size) {
3406  gobj_cat.info()
3407  << "Automatically rescaling " << name;
3408  if (n != 0) {
3409  gobj_cat.info(false)
3410  << " mipmap level " << n;
3411  }
3412  gobj_cat.info(false)
3413  << " from " << pfm.get_x_size() << " by "
3414  << pfm.get_y_size() << " to " << x_size << " by "
3415  << y_size << "\n";
3416 
3417  PfmFile scaled(pfm);
3418  scaled.resize(x_size, y_size);
3420 
3421  convert_from_pfm(cdata->_ram_images[n]._image,
3422  do_get_expected_ram_mipmap_page_size(cdata, n), z,
3423  scaled, cdata->_num_components, cdata->_component_width);
3424  } else {
3425  // Now copy the pixel data from the PfmFile into our internal
3426  // cdata->_image component.
3427  convert_from_pfm(cdata->_ram_images[n]._image,
3428  do_get_expected_ram_mipmap_page_size(cdata, n), z,
3429  pfm, cdata->_num_components, cdata->_component_width);
3430  }
3432 
3433  return true;
3434 }
3435 
3436 /**
3437  * Internal method to load an image into a section of a texture page or mipmap
3438  * level.
3439  */
3440 bool Texture::
3441 do_load_sub_image(CData *cdata, const PNMImage &image, int x, int y, int z, int n) {
3442  nassertr(n >= 0 && (size_t)n < cdata->_ram_images.size(), false);
3443 
3444  int tex_x_size = do_get_expected_mipmap_x_size(cdata, n);
3445  int tex_y_size = do_get_expected_mipmap_y_size(cdata, n);
3446  int tex_z_size = do_get_expected_mipmap_z_size(cdata, n);
3447 
3448  nassertr(x >= 0 && x < tex_x_size, false);
3449  nassertr(y >= 0 && y < tex_y_size, false);
3450  nassertr(z >= 0 && z < tex_z_size, false);
3451 
3452  nassertr(image.get_x_size() + x <= tex_x_size, false);
3453  nassertr(image.get_y_size() + y <= tex_y_size, false);
3454 
3455  // Flip y
3456  y = cdata->_y_size - (image.get_y_size() + y);
3457 
3458  cdata->inc_image_modified();
3459  do_modify_ram_mipmap_image(cdata, n);
3460  convert_from_pnmimage(cdata->_ram_images[n]._image,
3461  do_get_expected_ram_mipmap_page_size(cdata, n),
3462  tex_x_size, x, y, z, image,
3463  cdata->_num_components, cdata->_component_width);
3464 
3465  return true;
3466 }
3467 
3468 /**
3469  * Called internally when read() detects a txo file. Assumes the lock is
3470  * already held.
3471  */
3472 bool Texture::
3473 do_read_txo_file(CData *cdata, const Filename &fullpath) {
3475 
3476  Filename filename = Filename::binary_filename(fullpath);
3477  PT(VirtualFile) file = vfs->get_file(filename);
3478  if (file == nullptr) {
3479  // No such file.
3480  gobj_cat.error()
3481  << "Could not find " << fullpath << "\n";
3482  return false;
3483  }
3484 
3485  if (gobj_cat.is_debug()) {
3486  gobj_cat.debug()
3487  << "Reading texture object " << filename << "\n";
3488  }
3489 
3490  istream *in = file->open_read_file(true);
3491  bool success = do_read_txo(cdata, *in, fullpath);
3492  vfs->close_read_file(in);
3493 
3494  cdata->_fullpath = fullpath;
3495  cdata->_alpha_fullpath = Filename();
3496  cdata->_keep_ram_image = false;
3497 
3498  return success;
3499 }
3500 
3501 /**
3502  *
3503  */
3504 bool Texture::
3505 do_read_txo(CData *cdata, istream &in, const string &filename) {
3506  PT(Texture) other = make_from_txo(in, filename);
3507  if (other == nullptr) {
3508  return false;
3509  }
3510 
3511  CDReader cdata_other(other->_cycler);
3512  Namable::operator = (*other);
3513  do_assign(cdata, other, cdata_other);
3514 
3515  cdata->_loaded_from_image = true;
3516  cdata->_loaded_from_txo = true;
3517  cdata->_has_read_pages = false;
3518  cdata->_has_read_mipmaps = false;
3519  cdata->_num_mipmap_levels_read = 0;
3520  return true;
3521 }
3522 
3523 /**
3524  * Called internally when read() detects a DDS file. Assumes the lock is
3525  * already held.
3526  */
3527 bool Texture::
3528 do_read_dds_file(CData *cdata, const Filename &fullpath, bool header_only) {
3530 
3531  Filename filename = Filename::binary_filename(fullpath);
3532  PT(VirtualFile) file = vfs->get_file(filename);
3533  if (file == nullptr) {
3534  // No such file.
3535  gobj_cat.error()
3536  << "Could not find " << fullpath << "\n";
3537  return false;
3538  }
3539 
3540  if (gobj_cat.is_debug()) {
3541  gobj_cat.debug()
3542  << "Reading DDS file " << filename << "\n";
3543  }
3544 
3545  istream *in = file->open_read_file(true);
3546  bool success = do_read_dds(cdata, *in, fullpath, header_only);
3547  vfs->close_read_file(in);
3548 
3549  if (!has_name()) {
3550  set_name(fullpath.get_basename_wo_extension());
3551  }
3552 
3553  cdata->_fullpath = fullpath;
3554  cdata->_alpha_fullpath = Filename();
3555  cdata->_keep_ram_image = false;
3556 
3557  return success;
3558 }
3559 
3560 /**
3561  *
3562  */
3563 bool Texture::
3564 do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) {
3565  StreamReader dds(in);
3566 
3567  // DDS header (19 words)
3568  DDSHeader header;
3569  header.dds_magic = dds.get_uint32();
3570  header.dds_size = dds.get_uint32();
3571  header.dds_flags = dds.get_uint32();
3572  header.height = dds.get_uint32();
3573  header.width = dds.get_uint32();
3574  header.pitch = dds.get_uint32();
3575  header.depth = dds.get_uint32();
3576  header.num_levels = dds.get_uint32();
3577  dds.skip_bytes(44);
3578 
3579  // Pixelformat (8 words)
3580  header.pf.pf_size = dds.get_uint32();
3581  header.pf.pf_flags = dds.get_uint32();
3582  header.pf.four_cc = dds.get_uint32();
3583  header.pf.rgb_bitcount = dds.get_uint32();
3584  header.pf.r_mask = dds.get_uint32();
3585  header.pf.g_mask = dds.get_uint32();
3586  header.pf.b_mask = dds.get_uint32();
3587  header.pf.a_mask = dds.get_uint32();
3588 
3589  // Caps (4 words)
3590  header.caps.caps1 = dds.get_uint32();
3591  header.caps.caps2 = dds.get_uint32();
3592  header.caps.ddsx = dds.get_uint32();
3593  dds.skip_bytes(4);
3594 
3595  // Pad out to 32 words
3596  dds.skip_bytes(4);
3597 
3598  if (header.dds_magic != DDS_MAGIC || (in.fail() || in.eof())) {
3599  gobj_cat.error()
3600  << filename << " is not a DDS file.\n";
3601  return false;
3602  }
3603 
3604  if ((header.dds_flags & DDSD_MIPMAPCOUNT) == 0) {
3605  // No bit set means only the base mipmap level.
3606  header.num_levels = 1;
3607 
3608  } else if (header.num_levels == 0) {
3609  // Some files seem to have this set to 0 for some reason--existing readers
3610  // assume 0 means 1.
3611  header.num_levels = 1;
3612  }
3613 
3614  TextureType texture_type;
3615  if (header.caps.caps2 & DDSCAPS2_CUBEMAP) {
3616  static const unsigned int all_faces =
3617  (DDSCAPS2_CUBEMAP_POSITIVEX |
3618  DDSCAPS2_CUBEMAP_POSITIVEY |
3619  DDSCAPS2_CUBEMAP_POSITIVEZ |
3620  DDSCAPS2_CUBEMAP_NEGATIVEX |
3621  DDSCAPS2_CUBEMAP_NEGATIVEY |
3622  DDSCAPS2_CUBEMAP_NEGATIVEZ);
3623  if ((header.caps.caps2 & all_faces) != all_faces) {
3624  gobj_cat.error()
3625  << filename << " is missing some cube map faces; cannot load.\n";
3626  return false;
3627  }
3628  header.depth = 6;
3629  texture_type = TT_cube_map;
3630 
3631  } else if (header.caps.caps2 & DDSCAPS2_VOLUME) {
3632  texture_type = TT_3d_texture;
3633 
3634  } else {
3635  texture_type = TT_2d_texture;
3636  header.depth = 1;
3637  }
3638 
3639  // Determine the function to use to read the DDS image.
3640  typedef PTA_uchar (*ReadDDSLevelFunc)(Texture *tex, Texture::CData *cdata,
3641  const DDSHeader &header, int n, istream &in);
3642  ReadDDSLevelFunc func = nullptr;
3643 
3644  Format format = F_rgb;
3645  ComponentType component_type = T_unsigned_byte;
3646 
3647  do_clear_ram_image(cdata);
3648  CompressionMode compression = CM_off;
3649 
3650  if ((header.pf.pf_flags & DDPF_FOURCC) != 0 &&
3651  header.pf.four_cc == 0x30315844) { // 'DX10'
3652  // A DirectX 10 style texture, which has an additional header.
3653  func = read_dds_level_generic_uncompressed;
3654  unsigned int dxgi_format = dds.get_uint32();
3655  unsigned int dimension = dds.get_uint32();
3656  unsigned int misc_flag = dds.get_uint32();
3657  unsigned int array_size = dds.get_uint32();
3658  /*unsigned int alpha_mode = */dds.get_uint32();
3659 
3660  switch (dxgi_format) {
3661  case 2: // DXGI_FORMAT_R32G32B32A32_FLOAT
3662  format = F_rgba32;
3663  component_type = T_float;
3664  func = read_dds_level_abgr32;
3665  break;
3666  case 10: // DXGI_FORMAT_R16G16B16A16_FLOAT
3667  format = F_rgba16;
3668  component_type = T_half_float;
3669  func = read_dds_level_abgr16;
3670  break;
3671  case 11: // DXGI_FORMAT_R16G16B16A16_UNORM
3672  format = F_rgba16;
3673  component_type = T_unsigned_short;
3674  func = read_dds_level_abgr16;
3675  break;
3676  case 16: // DXGI_FORMAT_R32G32_FLOAT
3677  format = F_rg32;
3678  component_type = T_float;
3679  func = read_dds_level_raw;
3680  break;
3681  case 27: // DXGI_FORMAT_R8G8B8A8_TYPELESS
3682  case 28: // DXGI_FORMAT_R8G8B8A8_UNORM
3683  format = F_rgba8;
3684  func = read_dds_level_abgr8;
3685  break;
3686  case 29: // DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
3687  format = F_srgb_alpha;
3688  func = read_dds_level_abgr8;
3689  break;
3690  case 30: // DXGI_FORMAT_R8G8B8A8_UINT
3691  format = F_rgba8i;
3692  func = read_dds_level_abgr8;
3693  break;
3694  case 31: // DXGI_FORMAT_R8G8B8A8_SNORM
3695  format = F_rgba8;
3696  component_type = T_byte;
3697  func = read_dds_level_abgr8;
3698  break;
3699  case 32: // DXGI_FORMAT_R8G8B8A8_SINT
3700  format = F_rgba8i;
3701  component_type = T_byte;
3702  func = read_dds_level_abgr8;
3703  break;
3704  case 34: // DXGI_FORMAT_R16G16_FLOAT:
3705  format = F_rg16;
3706  component_type = T_half_float;
3707  func = read_dds_level_raw;
3708  break;
3709  case 35: // DXGI_FORMAT_R16G16_UNORM:
3710  format = F_rg16;
3711  component_type = T_unsigned_short;
3712  func = read_dds_level_raw;
3713  break;
3714  case 37: // DXGI_FORMAT_R16G16_SNORM:
3715  format = F_rg16;
3716  component_type = T_short;
3717  func = read_dds_level_raw;
3718  break;
3719  case 40: // DXGI_FORMAT_D32_FLOAT
3720  format = F_depth_component32;
3721  component_type = T_float;
3722  func = read_dds_level_raw;
3723  break;
3724  case 41: // DXGI_FORMAT_R32_FLOAT
3725  format = F_r32;
3726  component_type = T_float;
3727  func = read_dds_level_raw;
3728  break;
3729  case 42: // DXGI_FORMAT_R32_UINT
3730  format = F_r32i;
3731  component_type = T_unsigned_int;
3732  func = read_dds_level_raw;
3733  break;
3734  case 43: // DXGI_FORMAT_R32_SINT
3735  format = F_r32i;
3736  component_type = T_int;
3737  func = read_dds_level_raw;
3738  break;
3739  case 48: // DXGI_FORMAT_R8G8_TYPELESS
3740  case 49: // DXGI_FORMAT_R8G8_UNORM
3741  format = F_rg;
3742  break;
3743  case 50: // DXGI_FORMAT_R8G8_UINT
3744  format = F_rg8i;
3745  break;
3746  case 51: // DXGI_FORMAT_R8G8_SNORM
3747  format = F_rg;
3748  component_type = T_byte;
3749  break;
3750  case 52: // DXGI_FORMAT_R8G8_SINT
3751  format = F_rg8i;
3752  component_type = T_byte;
3753  break;
3754  case 54: // DXGI_FORMAT_R16_FLOAT:
3755  format = F_r16;
3756  component_type = T_half_float;
3757  func = read_dds_level_raw;
3758  break;
3759  case 55: // DXGI_FORMAT_D16_UNORM:
3760  format = F_depth_component16;
3761  component_type = T_unsigned_short;
3762  func = read_dds_level_raw;
3763  break;
3764  case 56: // DXGI_FORMAT_R16_UNORM:
3765  format = F_r16;
3766  component_type = T_unsigned_short;
3767  func = read_dds_level_raw;
3768  break;
3769  case 57: // DXGI_FORMAT_R16_UINT:
3770  format = F_r16i;
3771  component_type = T_unsigned_short;
3772  func = read_dds_level_raw;
3773  break;
3774  case 58: // DXGI_FORMAT_R16_SNORM:
3775  format = F_r16;
3776  component_type = T_short;
3777  func = read_dds_level_raw;
3778  break;
3779  case 59: // DXGI_FORMAT_R16_SINT:
3780  format = F_r16i;
3781  component_type = T_short;
3782  func = read_dds_level_raw;
3783  break;
3784  case 60: // DXGI_FORMAT_R8_TYPELESS
3785  case 61: // DXGI_FORMAT_R8_UNORM
3786  format = F_red;
3787  break;
3788  case 62: // DXGI_FORMAT_R8_UINT
3789  format = F_r8i;
3790  break;
3791  case 63: // DXGI_FORMAT_R8_SNORM
3792  format = F_red;
3793  component_type = T_byte;
3794  break;
3795  case 64: // DXGI_FORMAT_R8_SINT
3796  format = F_r8i;
3797  component_type = T_byte;
3798  break;
3799  case 65: // DXGI_FORMAT_A8_UNORM
3800  format = F_alpha;
3801  break;
3802  case 70: // DXGI_FORMAT_BC1_TYPELESS
3803  case 71: // DXGI_FORMAT_BC1_UNORM
3804  format = F_rgb;
3805  compression = CM_dxt1;
3806  func = read_dds_level_bc1;
3807  break;
3808  case 72: // DXGI_FORMAT_BC1_UNORM_SRGB
3809  format = F_srgb;
3810  compression = CM_dxt1;
3811  func = read_dds_level_bc1;
3812  break;
3813  case 73: // DXGI_FORMAT_BC2_TYPELESS
3814  case 74: // DXGI_FORMAT_BC2_UNORM
3815  format = F_rgba;
3816  compression = CM_dxt3;
3817  func = read_dds_level_bc2;
3818  break;
3819  case 75: // DXGI_FORMAT_BC2_UNORM_SRGB
3820  format = F_srgb_alpha;
3821  compression = CM_dxt3;
3822  func = read_dds_level_bc2;
3823  break;
3824  case 76: // DXGI_FORMAT_BC3_TYPELESS
3825  case 77: // DXGI_FORMAT_BC3_UNORM
3826  format = F_rgba;
3827  compression = CM_dxt5;
3828  func = read_dds_level_bc3;
3829  break;
3830  case 78: // DXGI_FORMAT_BC3_UNORM_SRGB
3831  format = F_srgb_alpha;
3832  compression = CM_dxt5;
3833  func = read_dds_level_bc3;
3834  break;
3835  case 79: // DXGI_FORMAT_BC4_TYPELESS
3836  case 80: // DXGI_FORMAT_BC4_UNORM
3837  format = F_red;
3838  compression = CM_rgtc;
3839  func = read_dds_level_bc4;
3840  break;
3841  case 82: // DXGI_FORMAT_BC5_TYPELESS
3842  case 83: // DXGI_FORMAT_BC5_UNORM
3843  format = F_rg;
3844  compression = CM_rgtc;
3845  func = read_dds_level_bc5;
3846  break;
3847  case 87: // DXGI_FORMAT_B8G8R8A8_UNORM
3848  case 90: // DXGI_FORMAT_B8G8R8A8_TYPELESS
3849  format = F_rgba8;
3850  break;
3851  case 88: // DXGI_FORMAT_B8G8R8X8_UNORM
3852  case 92: // DXGI_FORMAT_B8G8R8X8_TYPELESS
3853  format = F_rgb8;
3854  break;
3855  case 91: // DXGI_FORMAT_B8G8R8A8_UNORM_SRGB
3856  format = F_srgb_alpha;
3857  break;
3858  case 93: // DXGI_FORMAT_B8G8R8X8_UNORM_SRGB
3859  format = F_srgb;
3860  break;
3861  case 115: // DXGI_FORMAT_B4G4R4A4_UNORM
3862  format = F_rgba4;
3863  break;
3864  default:
3865  gobj_cat.error()
3866  << filename << ": unsupported DXGI format " << dxgi_format << ".\n";
3867  return false;
3868  }
3869 
3870  switch (dimension) {
3871  case 2: // DDS_DIMENSION_TEXTURE1D
3872  texture_type = TT_1d_texture;
3873  header.depth = 1;
3874  break;
3875  case 3: // DDS_DIMENSION_TEXTURE2D
3876  if (misc_flag & 0x4) { // DDS_RESOURCE_MISC_TEXTURECUBE
3877  if (array_size > 1) {
3878  texture_type = TT_cube_map_array;
3879  header.depth = array_size * 6;
3880  } else {
3881  texture_type = TT_cube_map;
3882  header.depth = 6;
3883  }
3884  } else {
3885  if (array_size > 1) {
3886  texture_type = TT_2d_texture_array;
3887  header.depth = array_size;
3888  } else {
3889  texture_type = TT_2d_texture;
3890  header.depth = 1;
3891  }
3892  }
3893  break;
3894  case 4: // DDS_DIMENSION_TEXTURE3D
3895  texture_type = TT_3d_texture;
3896  break;
3897  default:
3898  gobj_cat.error()
3899  << filename << ": unsupported dimension.\n";
3900  return false;
3901  }
3902 
3903  } else if (header.pf.pf_flags & DDPF_FOURCC) {
3904  // Some compressed texture format.
3905  if (texture_type == TT_3d_texture) {
3906  gobj_cat.error()
3907  << filename << ": unsupported compression on 3-d texture.\n";
3908  return false;
3909  }
3910 
3911  // Most of the compressed formats support alpha.
3912  format = F_rgba;
3913  switch (header.pf.four_cc) {
3914  case 0x31545844: // 'DXT1', little-endian.
3915  compression = CM_dxt1;
3916  func = read_dds_level_bc1;
3917  format = F_rgbm;
3918  break;
3919  case 0x32545844: // 'DXT2'
3920  compression = CM_dxt2;
3921  func = read_dds_level_bc2;
3922  break;
3923  case 0x33545844: // 'DXT3'
3924  compression = CM_dxt3;
3925  func = read_dds_level_bc2;
3926  break;
3927  case 0x34545844: // 'DXT4'
3928  compression = CM_dxt4;
3929  func = read_dds_level_bc3;
3930  break;
3931  case 0x35545844: // 'DXT5'
3932  compression = CM_dxt5;
3933  func = read_dds_level_bc3;
3934  break;
3935  case 0x31495441: // 'ATI1'
3936  case 0x55344342: // 'BC4U'
3937  compression = CM_rgtc;
3938  func = read_dds_level_bc4;
3939  format = F_red;
3940  break;
3941  case 0x32495441: // 'ATI2'
3942  case 0x55354342: // 'BC5U'
3943  compression = CM_rgtc;
3944  func = read_dds_level_bc5;
3945  format = F_rg;
3946  break;
3947  case 36: // D3DFMT_A16B16G16R16
3948  func = read_dds_level_abgr16;
3949  format = F_rgba16;
3950  component_type = T_unsigned_short;
3951  break;
3952  case 110: // D3DFMT_Q16W16V16U16
3953  func = read_dds_level_abgr16;
3954  format = F_rgba16;
3955  component_type = T_short;
3956  break;
3957  case 113: // D3DFMT_A16B16G16R16F
3958  func = read_dds_level_abgr16;
3959  format = F_rgba16;
3960  component_type = T_half_float;
3961  break;
3962  case 116: // D3DFMT_A32B32G32R32F
3963  func = read_dds_level_abgr32;
3964  format = F_rgba32;
3965  component_type = T_float;
3966  break;
3967  default:
3968  gobj_cat.error()
3969  << filename << ": unsupported texture compression (FourCC: 0x"
3970  << std::hex << header.pf.four_cc << std::dec << ").\n";
3971  return false;
3972  }
3973 
3974  } else {
3975  // An uncompressed texture format.
3976  func = read_dds_level_generic_uncompressed;
3977 
3978  if (header.pf.pf_flags & DDPF_ALPHAPIXELS) {
3979  // An uncompressed format that involves alpha.
3980  format = F_rgba;
3981  if (header.pf.rgb_bitcount == 32 &&
3982  header.pf.r_mask == 0x000000ff &&
3983  header.pf.g_mask == 0x0000ff00 &&
3984  header.pf.b_mask == 0x00ff0000 &&
3985  header.pf.a_mask == 0xff000000U) {
3986  func = read_dds_level_abgr8;
3987  } else if (header.pf.rgb_bitcount == 32 &&
3988  header.pf.r_mask == 0x00ff0000 &&
3989  header.pf.g_mask == 0x0000ff00 &&
3990  header.pf.b_mask == 0x000000ff &&
3991  header.pf.a_mask == 0xff000000U) {
3992  func = read_dds_level_rgba8;
3993 
3994  } else if (header.pf.r_mask != 0 &&
3995  header.pf.g_mask == 0 &&
3996  header.pf.b_mask == 0) {
3997  func = read_dds_level_luminance_uncompressed;
3998  format = F_luminance_alpha;
3999  }
4000  } else {
4001  // An uncompressed format that doesn't involve alpha.
4002  if (header.pf.rgb_bitcount == 24 &&
4003  header.pf.r_mask == 0x00ff0000 &&
4004  header.pf.g_mask == 0x0000ff00 &&
4005  header.pf.b_mask == 0x000000ff) {
4006  func = read_dds_level_bgr8;
4007  } else if (header.pf.rgb_bitcount == 24 &&
4008  header.pf.r_mask == 0x000000ff &&
4009  header.pf.g_mask == 0x0000ff00 &&
4010  header.pf.b_mask == 0x00ff0000) {
4011  func = read_dds_level_rgb8;
4012 
4013  } else if (header.pf.r_mask != 0 &&
4014  header.pf.g_mask == 0 &&
4015  header.pf.b_mask == 0) {
4016  func = read_dds_level_luminance_uncompressed;
4017  format = F_luminance;
4018  }
4019  }
4020  }
4021 
4022  do_setup_texture(cdata, texture_type, header.width, header.height, header.depth,
4023  component_type, format);
4024 
4025  cdata->_orig_file_x_size = cdata->_x_size;
4026  cdata->_orig_file_y_size = cdata->_y_size;
4027  cdata->_compression = compression;
4028  cdata->_ram_image_compression = compression;
4029 
4030  if (!header_only) {
4031  switch (texture_type) {
4032  case TT_3d_texture:
4033  {
4034  // 3-d textures store all the depth slices for mipmap level 0, then
4035  // all the depth slices for mipmap level 1, and so on.
4036  for (int n = 0; n < (int)header.num_levels; ++n) {
4037  int z_size = do_get_expected_mipmap_z_size(cdata, n);
4038  pvector<PTA_uchar> pages;
4039  size_t page_size = 0;
4040  int z;
4041  for (z = 0; z < z_size; ++z) {
4042  PTA_uchar page = func(this, cdata, header, n, in);
4043  if (page.is_null()) {
4044  return false;
4045  }
4046  nassertr(page_size == 0 || page_size == page.size(), false);
4047  page_size = page.size();
4048  pages.push_back(page);
4049  }
4050  // Now reassemble the pages into one big image. Because this is a
4051  // Microsoft format, the images are stacked in reverse order; re-
4052  // reverse them.
4053  PTA_uchar image = PTA_uchar::empty_array(page_size * z_size);
4054  unsigned char *imagep = (unsigned char *)image.p();
4055  for (z = 0; z < z_size; ++z) {
4056  int fz = z_size - 1 - z;
4057  memcpy(imagep + z * page_size, pages[fz].p(), page_size);
4058  }
4059 
4060  do_set_ram_mipmap_image(cdata, n, image, page_size);
4061  }
4062  }
4063  break;
4064 
4065  case TT_cube_map:
4066  {
4067  // Cube maps store all the mipmap levels for face 0, then all the
4068  // mipmap levels for face 1, and so on.
4070  pages.reserve(6);
4071  int z, n;
4072  for (z = 0; z < 6; ++z) {
4073  pages.push_back(pvector<PTA_uchar>());
4074  pvector<PTA_uchar> &levels = pages.back();
4075  levels.reserve(header.num_levels);
4076 
4077  for (n = 0; n < (int)header.num_levels; ++n) {
4078  PTA_uchar image = func(this, cdata, header, n, in);
4079  if (image.is_null()) {
4080  return false;
4081  }
4082  levels.push_back(image);
4083  }
4084  }
4085 
4086  // Now, for each level, reassemble the pages into one big image.
4087  // Because this is a Microsoft format, the levels are arranged in a
4088  // rotated order.
4089  static const int level_remap[6] = {
4090  0, 1, 5, 4, 2, 3
4091  };
4092  for (n = 0; n < (int)header.num_levels; ++n) {
4093  size_t page_size = pages[0][n].size();
4094  PTA_uchar image = PTA_uchar::empty_array(page_size * 6);
4095  unsigned char *imagep = (unsigned char *)image.p();
4096  for (z = 0; z < 6; ++z) {
4097  int fz = level_remap[z];
4098  nassertr(pages[fz][n].size() == page_size, false);
4099  memcpy(imagep + z * page_size, pages[fz][n].p(), page_size);
4100  }
4101 
4102  do_set_ram_mipmap_image(cdata, n, image, page_size);
4103  }
4104  }
4105  break;
4106 
4107  case TT_2d_texture_array:
4108  case TT_cube_map_array: //TODO: rearrange cube map array faces?
4109  {
4110  // Texture arrays store all the mipmap levels for layer 0, then all
4111  // the mipmap levels for layer 1, and so on.
4113  pages.reserve(header.depth);
4114  int z, n;
4115  for (z = 0; z < (int)header.depth; ++z) {
4116  pages.push_back(pvector<PTA_uchar>());
4117  pvector<PTA_uchar> &levels = pages.back();
4118  levels.reserve(header.num_levels);
4119 
4120  for (n = 0; n < (int)header.num_levels; ++n) {
4121  PTA_uchar image = func(this, cdata, header, n, in);
4122  if (image.is_null()) {
4123  return false;
4124  }
4125  levels.push_back(image);
4126  }
4127  }
4128 
4129  // Now, for each level, reassemble the pages into one big image.
4130  for (n = 0; n < (int)header.num_levels; ++n) {
4131  size_t page_size = pages[0][n].size();
4132  PTA_uchar image = PTA_uchar::empty_array(page_size * header.depth);
4133  unsigned char *imagep = (unsigned char *)image.p();
4134  for (z = 0; z < (int)header.depth; ++z) {
4135  nassertr(pages[z][n].size() == page_size, false);
4136  memcpy(imagep + z * page_size, pages[z][n].p(), page_size);
4137  }
4138 
4139  do_set_ram_mipmap_image(cdata, n, image, page_size);
4140  }
4141  }
4142  break;
4143 
4144  default:
4145  // Normal 2-d textures simply store the mipmap levels.
4146  {
4147  for (int n = 0; n < (int)header.num_levels; ++n) {
4148  PTA_uchar image = func(this, cdata, header, n, in);
4149  if (image.is_null()) {
4150  return false;
4151  }
4152  do_set_ram_mipmap_image(cdata, n, image, 0);
4153  }
4154  }
4155  }
4156  cdata->_has_read_pages = true;
4157  cdata->_has_read_mipmaps = true;
4158  cdata->_num_mipmap_levels_read = cdata->_ram_images.size();
4159  }
4160 
4161  if (in.fail()) {
4162  gobj_cat.error()
4163  << filename << ": truncated DDS file.\n";
4164  return false;
4165  }
4166 
4167  cdata->_loaded_from_image = true;
4168  cdata->_loaded_from_txo = true;
4169 
4170  return true;
4171 }
4172 
4173 /**
4174  * Called internally when read() detects a KTX file. Assumes the lock is
4175  * already held.
4176  */
4177 bool Texture::
4178 do_read_ktx_file(CData *cdata, const Filename &fullpath, bool header_only) {
4180 
4181  Filename filename = Filename::binary_filename(fullpath);
4182  PT(VirtualFile) file = vfs->get_file(filename);
4183  if (file == nullptr) {
4184  // No such file.
4185  gobj_cat.error()
4186  << "Could not find " << fullpath << "\n";
4187  return false;
4188  }
4189 
4190  if (gobj_cat.is_debug()) {
4191  gobj_cat.debug()
4192  << "Reading KTX file " << filename << "\n";
4193  }
4194 
4195  istream *in = file->open_read_file(true);
4196  bool success = do_read_ktx(cdata, *in, fullpath, header_only);
4197  vfs->close_read_file(in);
4198 
4199  if (!has_name()) {
4200  set_name(fullpath.get_basename_wo_extension());
4201  }
4202 
4203  cdata->_fullpath = fullpath;
4204  cdata->_alpha_fullpath = Filename();
4205  cdata->_keep_ram_image = false;
4206 
4207  return success;
4208 }
4209 
4210 /**
4211  *
4212  */
4213 bool Texture::
4214 do_read_ktx(CData *cdata, istream &in, const string &filename, bool header_only) {
4215  StreamReader ktx(in);
4216 
4217  unsigned char magic[12];
4218  if (ktx.extract_bytes(magic, 12) != 12 ||
4219  memcmp(magic, "\xABKTX 11\xBB\r\n\x1A\n", 12) != 0) {
4220  gobj_cat.error()
4221  << filename << " is not a KTX file.\n";
4222  return false;
4223  }
4224 
4225  // See: https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
4226  uint32_t gl_type, /*type_size,*/ gl_format, internal_format, gl_base_format,
4227  width, height, depth, num_array_elements, num_faces, num_mipmap_levels,
4228  kvdata_size;
4229 
4230  bool big_endian;
4231  if (ktx.get_uint32() == 0x04030201) {
4232  big_endian = false;
4233  gl_type = ktx.get_uint32();
4234  /*type_size = */ktx.get_uint32();
4235  gl_format = ktx.get_uint32();
4236  internal_format = ktx.get_uint32();
4237  gl_base_format = ktx.get_uint32();
4238  width = ktx.get_uint32();
4239  height = ktx.get_uint32();
4240  depth = ktx.get_uint32();
4241  num_array_elements = ktx.get_uint32();
4242  num_faces = ktx.get_uint32();
4243  num_mipmap_levels = ktx.get_uint32();
4244  kvdata_size = ktx.get_uint32();
4245  } else {
4246  big_endian = true;
4247  gl_type = ktx.get_be_uint32();
4248  /*type_size = */ktx.get_be_uint32();
4249  gl_format = ktx.get_be_uint32();
4250  internal_format = ktx.get_be_uint32();
4251  gl_base_format = ktx.get_be_uint32();
4252  width = ktx.get_be_uint32();
4253  height = ktx.get_be_uint32();
4254  depth = ktx.get_be_uint32();
4255  num_array_elements = ktx.get_be_uint32();
4256  num_faces = ktx.get_be_uint32();
4257  num_mipmap_levels = ktx.get_be_uint32();
4258  kvdata_size = ktx.get_be_uint32();
4259  }
4260 
4261  // Skip metadata section.
4262  ktx.skip_bytes(kvdata_size);
4263 
4264  ComponentType type;
4265  CompressionMode compression;
4266  Format format;
4267  bool swap_bgr = false;
4268 
4269  if (gl_type == 0 || gl_format == 0) {
4270  // Compressed texture.
4271  if (gl_type > 0 || gl_format > 0) {
4272  gobj_cat.error()
4273  << "Compressed textures must have both type and format set to 0.\n";
4274  return false;
4275  }
4276  type = T_unsigned_byte;
4277  compression = CM_on;
4278 
4279  KTXFormat base_format;
4280  switch ((KTXCompressedFormat)internal_format) {
4281  case KTX_COMPRESSED_RED:
4282  format = F_red;
4283  base_format = KTX_RED;
4284  break;
4285  case KTX_COMPRESSED_RG:
4286  format = F_rg;
4287  base_format = KTX_RG;
4288  break;
4289  case KTX_COMPRESSED_RGB:
4290  format = F_rgb;
4291  base_format = KTX_RGB;
4292  break;
4293  case KTX_COMPRESSED_RGBA:
4294  format = F_rgba;
4295  base_format = KTX_RGBA;
4296  break;
4297  case KTX_COMPRESSED_SRGB:
4298  format = F_srgb;
4299  base_format = KTX_SRGB;
4300  break;
4301  case KTX_COMPRESSED_SRGB_ALPHA:
4302  format = F_srgb_alpha;
4303  base_format = KTX_SRGB_ALPHA;
4304  break;
4305  case KTX_COMPRESSED_RGB_FXT1_3DFX:
4306  format = F_rgb;
4307  base_format = KTX_RGB;
4308  compression = CM_fxt1;
4309  break;
4310  case KTX_COMPRESSED_RGBA_FXT1_3DFX:
4311  format = F_rgba;
4312  base_format = KTX_RGBA;
4313  compression = CM_fxt1;
4314  break;
4315  case KTX_COMPRESSED_RGB_S3TC_DXT1:
4316  format = F_rgb;
4317  base_format = KTX_RGB;
4318  compression = CM_dxt1;
4319  break;
4320  case KTX_COMPRESSED_RGBA_S3TC_DXT1:
4321  format = F_rgbm;
4322  base_format = KTX_RGB;
4323  compression = CM_dxt1;
4324  break;
4325  case KTX_COMPRESSED_RGBA_S3TC_DXT3:
4326  format = F_rgba;
4327  base_format = KTX_RGBA;
4328  compression = CM_dxt3;
4329  break;
4330  case KTX_COMPRESSED_RGBA_S3TC_DXT5:
4331  format = F_rgba;
4332  base_format = KTX_RGBA;
4333  compression = CM_dxt5;
4334  break;
4335  case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT1:
4336  format = F_srgb_alpha;
4337  base_format = KTX_SRGB_ALPHA;
4338  compression = CM_dxt1;
4339  break;
4340  case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT3:
4341  format = F_srgb_alpha;
4342  base_format = KTX_SRGB_ALPHA;
4343  compression = CM_dxt3;
4344  break;
4345  case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT5:
4346  format = F_srgb_alpha;
4347  base_format = KTX_SRGB_ALPHA;
4348  compression = CM_dxt5;
4349  break;
4350  case KTX_COMPRESSED_SRGB_S3TC_DXT1:
4351  format = F_srgb;
4352  base_format = KTX_SRGB;
4353  compression = CM_dxt1;
4354  break;
4355  case KTX_COMPRESSED_RED_RGTC1:
4356  case KTX_COMPRESSED_SIGNED_RED_RGTC1:
4357  format = F_red;
4358  base_format = KTX_RED;
4359  compression = CM_rgtc;
4360  break;
4361  case KTX_COMPRESSED_RG_RGTC2:
4362  case KTX_COMPRESSED_SIGNED_RG_RGTC2:
4363  format = F_rg;
4364  base_format = KTX_RG;
4365  compression = CM_rgtc;
4366  break;
4367  case KTX_ETC1_RGB8:
4368  format = F_rgb;
4369  base_format = KTX_RGB;
4370  compression = CM_etc1;
4371  break;
4372  case KTX_ETC1_SRGB8:
4373  format = F_srgb;
4374  base_format = KTX_SRGB;
4375  compression = CM_etc1;
4376  break;
4377  case KTX_COMPRESSED_RGB8_ETC2:
4378  format = F_rgb;
4379  base_format = KTX_RGB;
4380  compression = CM_etc2;
4381  break;
4382  case KTX_COMPRESSED_SRGB8_ETC2:
4383  format = F_srgb;
4384  base_format = KTX_SRGB;
4385  compression = CM_etc2;
4386  break;
4387  case KTX_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
4388  format = F_rgbm;
4389  base_format = KTX_RGBA;
4390  compression = CM_etc2;
4391  break;
4392  case KTX_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
4393  format = F_rgbm;
4394  base_format = KTX_SRGB8_ALPHA8;
4395  compression = CM_etc2;
4396  break;
4397  case KTX_COMPRESSED_RGBA8_ETC2_EAC:
4398  format = F_rgba;
4399  base_format = KTX_RGBA;
4400  compression = CM_etc2;
4401  break;
4402  case KTX_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
4403  format = F_srgb_alpha;
4404  base_format = KTX_SRGB8_ALPHA8;
4405  compression = CM_etc2;
4406  break;
4407  case KTX_COMPRESSED_R11_EAC:
4408  case KTX_COMPRESSED_SIGNED_R11_EAC:
4409  format = F_red;
4410  base_format = KTX_RED;
4411  compression = CM_eac;
4412  break;
4413  case KTX_COMPRESSED_RG11_EAC:
4414  case KTX_COMPRESSED_SIGNED_RG11_EAC:
4415  format = F_rg;
4416  base_format = KTX_RG;
4417  compression = CM_eac;
4418  break;
4419  case KTX_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1:
4420  format = F_srgb_alpha;
4421  base_format = KTX_SRGB_ALPHA;
4422  compression = CM_pvr1_2bpp;
4423  break;
4424  case KTX_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1:
4425  format = F_srgb_alpha;
4426  base_format = KTX_SRGB_ALPHA;
4427  compression = CM_pvr1_4bpp;
4428  break;
4429  case KTX_COMPRESSED_RGBA_BPTC_UNORM:
4430  case KTX_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
4431  case KTX_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
4432  case KTX_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
4433  default:
4434  gobj_cat.error()
4435  << filename << " has unsupported compressed internal format " << internal_format << "\n";
4436  return false;
4437  }
4438 
4439  if (base_format != gl_base_format) {
4440  gobj_cat.error()
4441  << filename << " has internal format that is incompatible with base "
4442  "format (0x" << std::hex << gl_base_format << ", expected 0x"
4443  << base_format << std::dec << ")\n";
4444  return false;
4445  }
4446 
4447  } else {
4448  // Uncompressed texture.
4449  compression = CM_off;
4450  switch ((KTXType)gl_type) {
4451  case KTX_BYTE:
4452  type = T_byte;
4453  break;
4454  case KTX_UNSIGNED_BYTE:
4455  type = T_unsigned_byte;
4456  break;
4457  case KTX_SHORT:
4458  type = T_short;
4459  break;
4460  case KTX_UNSIGNED_SHORT:
4461  type = T_unsigned_short;
4462  break;
4463  case KTX_INT:
4464  type = T_int;
4465  break;
4466  case KTX_UNSIGNED_INT:
4467  type = T_unsigned_int;
4468  break;
4469  case KTX_FLOAT:
4470  type = T_float;
4471  break;
4472  case KTX_HALF_FLOAT:
4473  type = T_half_float;
4474  break;
4475  case KTX_UNSIGNED_INT_24_8:
4476  type = T_unsigned_int_24_8;
4477  break;
4478  default:
4479  gobj_cat.error()
4480  << filename << " has unsupported component type " << gl_type << "\n";
4481  return false;
4482  }
4483 
4484  if (gl_format != gl_base_format) {
4485  gobj_cat.error()
4486  << filename << " has mismatched formats: " << gl_format << " != "
4487  << gl_base_format << "\n";
4488  }
4489 
4490  switch (gl_format) {
4491  case KTX_DEPTH_COMPONENT:
4492  switch (internal_format) {
4493  case KTX_DEPTH_COMPONENT:
4494  format = F_depth_component;
4495  break;
4496  case KTX_DEPTH_COMPONENT16:
4497  format = F_depth_component16;
4498  break;
4499  case KTX_DEPTH_COMPONENT24:
4500  format = F_depth_component24;
4501  break;
4502  case KTX_DEPTH_COMPONENT32:
4503  case KTX_DEPTH_COMPONENT32F:
4504  format = F_depth_component32;
4505  break;
4506  default:
4507  format = F_depth_component;
4508  gobj_cat.warning()
4509  << filename << " has unsupported depth component format " << internal_format << "\n";
4510  }
4511  break;
4512 
4513  case KTX_DEPTH_STENCIL:
4514  format = F_depth_stencil;
4515  if (internal_format != KTX_DEPTH_STENCIL &&
4516  internal_format != KTX_DEPTH24_STENCIL8) {
4517  gobj_cat.warning()
4518  << filename << " has unsupported depth stencil format " << internal_format << "\n";
4519  }
4520  break;
4521 
4522  case KTX_RED:
4523  switch (internal_format) {
4524  case KTX_RED:
4525  case KTX_RED_SNORM:
4526  case KTX_R8:
4527  case KTX_R8_SNORM:
4528  format = F_red;
4529  break;
4530  case KTX_R16:
4531  case KTX_R16_SNORM:
4532  case KTX_R16F:
4533  format = F_r16;
4534  break;
4535  case KTX_R32F:
4536  format = F_r32;
4537  break;
4538  default:
4539  format = F_red;
4540  gobj_cat.warning()
4541  << filename << " has unsupported red format " << internal_format << "\n";
4542  }
4543  break;
4544 
4545  case KTX_RED_INTEGER:
4546  switch (internal_format) {
4547  case KTX_R8I:
4548  case KTX_R8UI:
4549  format = F_r8i;
4550  break;
4551  case KTX_R16I:
4552  case KTX_R16UI:
4553  format = F_r16i;
4554  break;
4555  case KTX_R32I:
4556  case KTX_R32UI:
4557  format = F_r32i;
4558  break;
4559  default:
4560  gobj_cat.error()
4561  << filename << " has unsupported red integer format " << internal_format << "\n";
4562  return false;
4563  }
4564  break;
4565 
4566  case KTX_GREEN:
4567  format = F_green;
4568  if (internal_format != KTX_GREEN) {
4569  gobj_cat.warning()
4570  << filename << " has unsupported green format " << internal_format << "\n";
4571  }
4572  break;
4573 
4574  case KTX_BLUE:
4575  format = F_blue;
4576  if (internal_format != KTX_BLUE) {
4577  gobj_cat.warning()
4578  << filename << " has unsupported blue format " << internal_format << "\n";
4579  }
4580  break;
4581 
4582  case KTX_RG:
4583  switch (internal_format) {
4584  case KTX_RG:
4585  case KTX_RG_SNORM:
4586  case KTX_RG8:
4587  case KTX_RG8_SNORM:
4588  format = F_rg;
4589  break;
4590  case KTX_RG16:
4591  case KTX_RG16_SNORM:
4592  case KTX_RG16F:
4593  format = F_rg16;
4594  break;
4595  case KTX_RG32F:
4596  format = F_rg32;
4597  break;
4598  default:
4599  format = F_rg;
4600  gobj_cat.warning()
4601  << filename << " has unsupported RG format " << internal_format << "\n";
4602  }
4603  break;
4604 
4605  case KTX_RG_INTEGER:
4606  switch (internal_format) {
4607  case KTX_RG8I:
4608  case KTX_RG8UI:
4609  format = F_rg8i;
4610  break;
4611  case KTX_RG16I:
4612  case KTX_RG16UI:
4613  case KTX_RG32I:
4614  case KTX_RG32UI:
4615  default:
4616  gobj_cat.error()
4617  << filename << " has unsupported RG integer format " << internal_format << "\n";
4618  return false;
4619  }
4620  break;
4621 
4622  case KTX_RGB:
4623  swap_bgr = true;
4624  case KTX_BGR:
4625  switch (internal_format) {
4626  case KTX_RGB:
4627  case KTX_RGB_SNORM:
4628  format = F_rgb;
4629  break;
4630  case KTX_RGB5:
4631  format = F_rgb5;
4632  break;
4633  case KTX_RGB12:
4634  format = F_rgb12;
4635  break;
4636  case KTX_R3_G3_B2:
4637  format = F_rgb332;
4638  break;
4639  case KTX_RGB9_E5:
4640  format = F_rgb9_e5;
4641  break;
4642  case KTX_R11F_G11F_B10F:
4643  format = F_r11_g11_b10;
4644  break;
4645  case KTX_RGB8:
4646  case KTX_RGB8_SNORM:
4647  format = F_rgb8;
4648  break;
4649  case KTX_RGB16:
4650  case KTX_RGB16_SNORM:
4651  case KTX_RGB16F:
4652  format = F_rgb16;
4653  break;
4654  case KTX_RGB32F:
4655  format = F_rgb32;
4656  break;
4657  case KTX_SRGB:
4658  case KTX_SRGB8:
4659  format = F_srgb;
4660  break;
4661  default:
4662  format = F_rgb;
4663  gobj_cat.warning()
4664  << filename << " has unsupported RGB format " << internal_format << "\n";
4665  }
4666  break;
4667 
4668  case KTX_RGB_INTEGER:
4669  swap_bgr = true;
4670  case KTX_BGR_INTEGER:
4671  switch (internal_format) {
4672  case KTX_RGB8I:
4673  case KTX_RGB8UI:
4674  format = F_rgb8i;
4675  break;
4676  case KTX_RGB16I:
4677  case KTX_RGB16UI:
4678  case KTX_RGB32I:
4679  case KTX_RGB32UI:
4680  default:
4681  gobj_cat.error()
4682  << filename << " has unsupported RGB integer format " << internal_format << "\n";
4683  return false;
4684  }
4685  break;
4686 
4687  case KTX_RGBA:
4688  swap_bgr = true;
4689  case KTX_BGRA:
4690  switch (internal_format) {
4691  case KTX_RGBA:
4692  case KTX_RGBA_SNORM:
4693  format = F_rgba;
4694  break;
4695  case KTX_RGBA4:
4696  format = F_rgba4;
4697  break;
4698  case KTX_RGB5_A1:
4699  format = F_rgba5;
4700  break;
4701  case KTX_RGBA12:
4702  format = F_rgba12;
4703  break;
4704  case KTX_RGB10_A2:
4705  format = F_rgb10_a2;
4706  break;
4707  case KTX_RGBA8:
4708  case KTX_RGBA8_SNORM:
4709  format = F_rgba8;
4710  break;
4711  case KTX_RGBA16:
4712  case KTX_RGBA16_SNORM:
4713  case KTX_RGBA16F:
4714  format = F_rgba16;
4715  break;
4716  case KTX_RGBA32F:
4717  format = F_rgba32;
4718  break;
4719  case KTX_SRGB_ALPHA:
4720  case KTX_SRGB8_ALPHA8:
4721  format = F_srgb_alpha;
4722  break;
4723  default:
4724  format = F_rgba;
4725  gobj_cat.warning()
4726  << filename << " has unsupported RGBA format " << internal_format << "\n";
4727  }
4728  break;
4729  break;
4730 
4731  case KTX_RGBA_INTEGER:
4732  swap_bgr = true;
4733  case KTX_BGRA_INTEGER:
4734  switch (internal_format) {
4735  case KTX_RGBA8I:
4736  case KTX_RGBA8UI:
4737  format = F_rgba8i;
4738  break;
4739  case KTX_RGBA16I:
4740  case KTX_RGBA16UI:
4741  case KTX_RGBA32I:
4742  case KTX_RGBA32UI:
4743  default:
4744  gobj_cat.error()
4745  << filename << " has unsupported RGBA integer format " << internal_format << "\n";
4746  return false;
4747  }
4748  break;
4749 
4750  case KTX_LUMINANCE:
4751  format = F_luminance;
4752  break;
4753 
4754  case KTX_LUMINANCE_ALPHA:
4755  format = F_luminance_alpha;
4756  break;
4757 
4758  case KTX_ALPHA:
4759  format = F_alpha;
4760  break;
4761 
4762  case KTX_STENCIL_INDEX:
4763  default:
4764  gobj_cat.error()
4765  << filename << " has unsupported format " << gl_format << "\n";
4766  return false;
4767  }
4768  }
4769 
4770  TextureType texture_type;
4771  if (depth > 0) {
4772  texture_type = TT_3d_texture;
4773 
4774  } else if (num_faces > 1) {
4775  if (num_faces != 6) {
4776  gobj_cat.error()
4777  << filename << " has " << num_faces << " cube map faces, expected 6\n";
4778  return false;
4779  }
4780  if (width != height) {
4781  gobj_cat.error()
4782  << filename << " is cube map, but does not have square dimensions\n";
4783  return false;
4784  }
4785  if (num_array_elements > 0) {
4786  depth = num_array_elements * 6;
4787  texture_type = TT_cube_map_array;
4788  } else {
4789  depth = 6;
4790  texture_type = TT_cube_map;
4791  }
4792 
4793  } else if (height > 0) {
4794  if (num_array_elements > 0) {
4795  depth = num_array_elements;
4796  texture_type = TT_2d_texture_array;
4797  } else {
4798  depth = 1;
4799  texture_type = TT_2d_texture;
4800  }
4801 
4802  } else if (width > 0) {
4803  depth = 1;
4804  if (num_array_elements > 0) {
4805  height = num_array_elements;
4806  texture_type = TT_1d_texture_array;
4807  } else {
4808  height = 1;
4809  texture_type = TT_1d_texture;
4810  }
4811 
4812  } else {
4813  gobj_cat.error()
4814  << filename << " has zero size\n";
4815  return false;
4816  }
4817 
4818  do_setup_texture(cdata, texture_type, width, height, depth, type, format);
4819 
4820  cdata->_orig_file_x_size = cdata->_x_size;
4821  cdata->_orig_file_y_size = cdata->_y_size;
4822  cdata->_compression = compression;
4823  cdata->_ram_image_compression = compression;
4824 
4825  if (!header_only) {
4826  bool generate_mipmaps = false;
4827  if (num_mipmap_levels == 0) {
4828  generate_mipmaps = true;
4829  num_mipmap_levels = 1;
4830  }
4831 
4832  for (uint32_t n = 0; n < num_mipmap_levels; ++n) {
4833  uint32_t image_size;
4834  if (big_endian) {
4835  image_size = ktx.get_be_uint32();
4836  } else {
4837  image_size = ktx.get_uint32();
4838  }
4839  PTA_uchar image;
4840 
4841  if (compression == CM_off) {
4842  uint32_t row_size = do_get_expected_mipmap_x_size(cdata, (int)n) * cdata->_num_components * cdata->_component_width;
4843  uint32_t num_rows = do_get_expected_mipmap_y_size(cdata, (int)n) * do_get_expected_mipmap_z_size(cdata, (int)n);
4844  uint32_t row_padded = (row_size + 3) & ~3;
4845 
4846  if (image_size == row_size * num_rows) {
4847  if (row_padded != row_size) {
4848  // Someone tightly packed the image. This is invalid, but because
4849  // we like it tightly packed too, we'll read it anyway.
4850  gobj_cat.warning()
4851  << filename << " does not have proper row padding for mipmap "
4852  "level " << n << "\n";
4853  }
4854  image = PTA_uchar::empty_array(image_size);
4855  ktx.extract_bytes(image.p(), image_size);
4856 
4857  } else if (image_size != row_padded * num_rows) {
4858  gobj_cat.error()
4859  << filename << " has invalid image size " << image_size
4860  << " for mipmap level " << n << " (expected "
4861  << row_padded * num_rows << ")\n";
4862  return false;
4863 
4864  } else {
4865  // Read it row by row.
4866  image = PTA_uchar::empty_array(row_size * num_rows);
4867  uint32_t skip = row_padded - row_size;
4868  unsigned char *p = image.p();
4869  for (uint32_t row = 0; row < num_rows; ++row) {
4870  ktx.extract_bytes(p, row_size);
4871  ktx.skip_bytes(skip);
4872  p += row_size;
4873  }
4874  }
4875 
4876  // Swap red and blue channels if necessary to match Panda conventions.
4877  if (swap_bgr) {
4878  unsigned char *begin = image.p();
4879  const unsigned char *end = image.p() + image.size();
4880  size_t skip = cdata->_num_components;
4881  nassertr(skip == 3 || skip == 4, false);
4882 
4883  switch (cdata->_component_width) {
4884  case 1:
4885  for (unsigned char *p = begin; p < end; p += skip) {
4886  swap(p[0], p[2]);
4887  }
4888  break;
4889  case 2:
4890  for (short *p = (short *)begin; p < (short *)end; p += skip) {
4891  swap(p[0], p[2]);
4892  }
4893  break;
4894  case 4:
4895  for (int *p = (int *)begin; p < (int *)end; p += skip) {
4896  swap(p[0], p[2]);
4897  }
4898  break;
4899  default:
4900  nassert_raise("unexpected channel count");
4901  return false;
4902  }
4903  }
4904 
4905  do_set_ram_mipmap_image(cdata, (int)n, std::move(image),
4906  row_size * do_get_expected_mipmap_y_size(cdata, (int)n));
4907 
4908  } else {
4909  // Compressed image. We'll trust that the file has the right size.
4910  image = PTA_uchar::empty_array(image_size);
4911  ktx.extract_bytes(image.p(), image_size);
4912  do_set_ram_mipmap_image(cdata, (int)n, std::move(image), image_size / depth);
4913  }
4914 
4915  ktx.skip_bytes(3 - ((image_size + 3) & 3));
4916  }
4917 
4918  cdata->_has_read_pages = true;
4919  cdata->_has_read_mipmaps = true;
4920  cdata->_num_mipmap_levels_read = cdata->_ram_images.size();
4921 
4922  if (generate_mipmaps) {
4923  do_generate_ram_mipmap_images(cdata, false);
4924  }
4925  }
4926 
4927  if (in.fail()) {
4928  gobj_cat.error()
4929  << filename << ": truncated KTX file.\n";
4930  return false;
4931  }
4932 
4933  cdata->_loaded_from_image = true;
4934  cdata->_loaded_from_txo = true;
4935 
4936  return true;
4937 }
4938 
4939 /**
4940  * Internal method to write a series of pages and/or mipmap levels to disk
4941  * files.
4942  */
4943 bool Texture::
4944 do_write(CData *cdata,
4945  const Filename &fullpath, int z, int n, bool write_pages, bool write_mipmaps) {
4946  if (is_txo_filename(fullpath)) {
4947  if (!do_has_bam_rawdata(cdata)) {
4948  do_get_bam_rawdata(cdata);
4949  }
4950  nassertr(do_has_bam_rawdata(cdata), false);
4951  return do_write_txo_file(cdata, fullpath);
4952  }
4953 
4954  if (!do_has_uncompressed_ram_image(cdata)) {
4955  do_get_uncompressed_ram_image(cdata);
4956  }
4957 
4958  nassertr(do_has_ram_mipmap_image(cdata, n), false);
4959  nassertr(cdata->_ram_image_compression == CM_off, false);
4960 
4961  if (write_pages && write_mipmaps) {
4962  // Write a sequence of pages * mipmap levels.
4963  Filename fullpath_pattern = Filename::pattern_filename(fullpath);
4964  int num_levels = cdata->_ram_images.size();
4965 
4966  for (int n = 0; n < num_levels; ++n) {
4967  int num_pages = do_get_expected_mipmap_num_pages(cdata, n);
4968 
4969  for (z = 0; z < num_pages; ++z) {
4970  Filename n_pattern = Filename::pattern_filename(fullpath_pattern.get_filename_index(z));
4971 
4972  if (!n_pattern.has_hash()) {
4973  gobj_cat.error()
4974  << "Filename requires two different hash sequences: " << fullpath
4975  << "\n";
4976  return false;
4977  }
4978 
4979  if (!do_write_one(cdata, n_pattern.get_filename_index(n), z, n)) {
4980  return false;
4981  }
4982  }
4983  }
4984 
4985  } else if (write_pages) {
4986  // Write a sequence of pages.
4987  Filename fullpath_pattern = Filename::pattern_filename(fullpath);
4988  if (!fullpath_pattern.has_hash()) {
4989  gobj_cat.error()
4990  << "Filename requires a hash mark: " << fullpath
4991  << "\n";
4992  return false;
4993  }
4994 
4995  int num_pages = cdata->_z_size * cdata->_num_views;
4996  for (z = 0; z < num_pages; ++z) {
4997  if (!do_write_one(cdata, fullpath_pattern.get_filename_index(z), z, n)) {
4998  return false;
4999  }
5000  }
5001 
5002  } else if (write_mipmaps) {
5003  // Write a sequence of mipmap images.
5004  Filename fullpath_pattern = Filename::pattern_filename(fullpath);
5005  if (!fullpath_pattern.has_hash()) {
5006  gobj_cat.error()
5007  << "Filename requires a hash mark: " << fullpath
5008  << "\n";
5009  return false;
5010  }
5011 
5012  int num_levels = cdata->_ram_images.size();
5013  for (int n = 0; n < num_levels; ++n) {
5014  if (!do_write_one(cdata, fullpath_pattern.get_filename_index(n), z, n)) {
5015  return false;
5016  }
5017  }
5018 
5019  } else {
5020  // Write a single file.
5021  if (!do_write_one(cdata, fullpath, z, n)) {
5022  return false;
5023  }
5024  }
5025 
5026  return true;
5027 }
5028 
5029 /**
5030  * Internal method to write the indicated page and mipmap level to a disk
5031  * image file.
5032  */
5033 bool Texture::
5034 do_write_one(CData *cdata, const Filename &fullpath, int z, int n) {
5035  if (!do_has_ram_mipmap_image(cdata, n)) {
5036  return false;
5037  }
5038 
5039  nassertr(cdata->_ram_image_compression == CM_off, false);
5040 
5041  bool success;
5042  if (cdata->_component_type == T_float) {
5043  // Writing a floating-point texture.
5044  PfmFile pfm;
5045  if (!do_store_one(cdata, pfm, z, n)) {
5046  return false;
5047  }
5048  success = pfm.write(fullpath);
5049  } else {
5050  // Writing a normal, integer texture.
5051  PNMImage pnmimage;
5052  if (!do_store_one(cdata, pnmimage, z, n)) {
5053  return false;
5054  }
5055  success = pnmimage.write(fullpath);
5056  }
5057 
5058  if (!success) {
5059  gobj_cat.error()
5060  << "Texture::write() - couldn't write: " << fullpath << endl;
5061  return false;
5062  }
5063 
5064  return true;
5065 }
5066 
5067 /**
5068  * Internal method to copy a page and/or mipmap level to a PNMImage.
5069  */
5070 bool Texture::
5071 do_store_one(CData *cdata, PNMImage &pnmimage, int z, int n) {
5072  // First, reload the ram image if necessary.
5073  do_get_uncompressed_ram_image(cdata);
5074 
5075  if (!do_has_ram_mipmap_image(cdata, n)) {
5076  return false;
5077  }
5078 
5079  nassertr(z >= 0 && z < do_get_expected_mipmap_num_pages(cdata, n), false);
5080  nassertr(cdata->_ram_image_compression == CM_off, false);
5081 
5082  if (cdata->_component_type == T_float) {
5083  // PNMImage by way of PfmFile.
5084  PfmFile pfm;
5085  bool success = convert_to_pfm(pfm,
5086  do_get_expected_mipmap_x_size(cdata, n),
5087  do_get_expected_mipmap_y_size(cdata, n),
5088  cdata->_num_components, cdata->_component_width,
5089  cdata->_ram_images[n]._image,
5090  do_get_ram_mipmap_page_size(cdata, n), z);
5091  if (!success) {
5092  return false;
5093  }
5094  return pfm.store(pnmimage);
5095  }
5096 
5097  return convert_to_pnmimage(pnmimage,
5098  do_get_expected_mipmap_x_size(cdata, n),
5099  do_get_expected_mipmap_y_size(cdata, n),
5100  cdata->_num_components, cdata->_component_type,
5101  is_srgb(cdata->_format),
5102  cdata->_ram_images[n]._image,
5103  do_get_ram_mipmap_page_size(cdata, n), z);
5104 }
5105 
5106 /**
5107  * Internal method to copy a page and/or mipmap level to a PfmFile.
5108  */
5109 bool Texture::
5110 do_store_one(CData *cdata, PfmFile &pfm, int z, int n) {
5111  // First, reload the ram image if necessary.
5112  do_get_uncompressed_ram_image(cdata);
5113 
5114  if (!do_has_ram_mipmap_image(cdata, n)) {
5115  return false;
5116  }
5117 
5118  nassertr(z >= 0 && z < do_get_expected_mipmap_num_pages(cdata, n), false);
5119  nassertr(cdata->_ram_image_compression == CM_off, false);
5120 
5121  if (cdata->_component_type != T_float) {
5122  // PfmFile by way of PNMImage.
5123  PNMImage pnmimage;
5124  bool success =
5125  convert_to_pnmimage(pnmimage,
5126  do_get_expected_mipmap_x_size(cdata, n),
5127  do_get_expected_mipmap_y_size(cdata, n),
5128  cdata->_num_components, cdata->_component_type,
5129  is_srgb(cdata->_format),
5130  cdata->_ram_images[n]._image,
5131  do_get_ram_mipmap_page_size(cdata, n), z);
5132  if (!success) {
5133  return false;
5134  }
5135  return pfm.load(pnmimage);
5136  }
5137 
5138  return convert_to_pfm(pfm,
5139  do_get_expected_mipmap_x_size(cdata, n),
5140  do_get_expected_mipmap_y_size(cdata, n),
5141  cdata->_num_components, cdata->_component_width,
5142  cdata->_ram_images[n]._image,
5143  do_get_ram_mipmap_page_size(cdata, n), z);
5144 }
5145 
5146 /**
5147  * Called internally when write() detects a txo filename.
5148  */
5149 bool Texture::
5150 do_write_txo_file(const CData *cdata, const Filename &fullpath) const {
5152  Filename filename = Filename::binary_filename(fullpath);
5153  ostream *out = vfs->open_write_file(filename, true, true);
5154  if (out == nullptr) {
5155  gobj_cat.error()
5156  << "Unable to open " << filename << "\n";
5157  return false;
5158  }
5159 
5160  bool success = do_write_txo(cdata, *out, fullpath);
5161  vfs->close_write_file(out);
5162  return success;
5163 }
5164 
5165 /**
5166  *
5167  */
5168 bool Texture::
5169 do_write_txo(const CData *cdata, ostream &out, const string &filename) const {
5170  DatagramOutputFile dout;
5171 
5172  if (!dout.open(out, filename)) {
5173  gobj_cat.error()
5174  << "Could not write texture object: " << filename << "\n";
5175  return false;
5176  }
5177 
5178  if (!dout.write_header(_bam_header)) {
5179  gobj_cat.error()
5180  << "Unable to write to " << filename << "\n";
5181  return false;
5182  }
5183 
5184  BamWriter writer(&dout);
5185  if (!writer.init()) {
5186  return false;
5187  }
5188 
5189  writer.set_file_texture_mode(BamWriter::BTM_rawdata);
5190 
5191  if (!writer.write_object(this)) {
5192  return false;
5193  }
5194 
5195  if (!do_has_bam_rawdata(cdata)) {
5196  gobj_cat.error()
5197  << get_name() << " does not have ram image\n";
5198  return false;
5199  }
5200 
5201  return true;
5202 }
5203 
5204 /**
5205  * If the texture has a ram image already, this acquires the CData write lock
5206  * and returns it.
5207  *
5208  * If the texture lacks a ram image, this performs do_reload_ram_image(), but
5209  * without holding the lock on this particular Texture object, to avoid
5210  * holding the lock across what might be a slow operation. Instead, the
5211  * reload is performed in a copy of the texture object, and then the lock is
5212  * acquired and the data is copied in.
5213  *
5214  * In any case, the return value is a locked CData object, which must be
5215  * released with an explicit call to release_write(). The CData object will
5216  * have a ram image unless for some reason do_reload_ram_image() fails.
5217  */
5218 Texture::CData *Texture::
5219 unlocked_ensure_ram_image(bool allow_compression) {
5220  Thread *current_thread = Thread::get_current_thread();
5221 
5222  // First, wait for any other threads that might be simultaneously performing
5223  // the same operation.
5224  MutexHolder holder(_lock);
5225  while (_reloading) {
5226  _cvar.wait();
5227  }
5228 
5229  // Then make sure we still need to reload before continuing.
5230  const CData *cdata = _cycler.read(current_thread);
5231  bool has_ram_image = do_has_ram_image(cdata);
5232  if (has_ram_image && !allow_compression && cdata->_ram_image_compression != Texture::CM_off) {
5233  // If we don't want compression, but the ram image we have is pre-
5234  // compressed, we don't consider it.
5235  has_ram_image = false;
5236  }
5237  if (has_ram_image || !do_can_reload(cdata)) {
5238  // We don't need to reload after all, or maybe we can't reload anyway.
5239  // Return, but elevate the lock first, as we promised.
5240  return _cycler.elevate_read_upstream(cdata, false, current_thread);
5241  }
5242 
5243  // We need to reload.
5244  nassertr(!_reloading, nullptr);
5245  _reloading = true;
5246 
5247  PT(Texture) tex = do_make_copy(cdata);
5248  _cycler.release_read(cdata);
5249  _lock.unlock();
5250 
5251  // Perform the actual reload in a copy of the texture, while our own mutex
5252  // is left unlocked.
5253  CDWriter cdata_tex(tex->_cycler, true);
5254  tex->do_reload_ram_image(cdata_tex, allow_compression);
5255 
5256  _lock.lock();
5257 
5258  CData *cdataw = _cycler.write_upstream(false, current_thread);
5259 
5260  // Rather than calling do_assign(), which would copy *all* of the reloaded
5261  // texture's properties over, we only copy in the ones which are relevant to
5262  // the ram image. This way, if the properties have changed during the
5263  // reload (for instance, because we reloaded a txo), it won't contaminate
5264  // the original texture.
5265  cdataw->_orig_file_x_size = cdata_tex->_orig_file_x_size;
5266  cdataw->_orig_file_y_size = cdata_tex->_orig_file_y_size;
5267 
5268  // If any of *these* properties have changed, the texture has changed in
5269  // some fundamental way. Update it appropriately.
5270  if (cdata_tex->_x_size != cdataw->_x_size ||
5271  cdata_tex->_y_size != cdataw->_y_size ||
5272  cdata_tex->_z_size != cdataw->_z_size ||
5273  cdata_tex->_num_views != cdataw->_num_views ||
5274  cdata_tex->_num_components != cdataw->_num_components ||
5275  cdata_tex->_component_width != cdataw->_component_width ||
5276  cdata_tex->_texture_type != cdataw->_texture_type ||
5277  cdata_tex->_component_type != cdataw->_component_type) {
5278 
5279  cdataw->_x_size = cdata_tex->_x_size;
5280  cdataw->_y_size = cdata_tex->_y_size;
5281  cdataw->_z_size = cdata_tex->_z_size;
5282  cdataw->_num_views = cdata_tex->_num_views;
5283 
5284  cdataw->_num_components = cdata_tex->_num_components;
5285  cdataw->_component_width = cdata_tex->_component_width;
5286  cdataw->_texture_type = cdata_tex->_texture_type;
5287  cdataw->_format = cdata_tex->_format;
5288  cdataw->_component_type = cdata_tex->_component_type;
5289 
5290  cdataw->inc_properties_modified();
5291  cdataw->inc_image_modified();
5292  }
5293 
5294  cdataw->_keep_ram_image = cdata_tex->_keep_ram_image;
5295  cdataw->_ram_image_compression = cdata_tex->_ram_image_compression;
5296  cdataw->_ram_images = cdata_tex->_ram_images;
5297 
5298  nassertr(_reloading, nullptr);
5299  _reloading = false;
5300 
5301  // We don't generally increment the cdata->_image_modified semaphore,
5302  // because this is just a reload, and presumably the image hasn't changed
5303  // (unless we hit the if condition above).
5304 
5305  _cvar.notify_all();
5306 
5307  // Return the still-locked cdata.
5308  return cdataw;
5309 }
5310 
5311 /**
5312  * Called when the Texture image is required but the ram image is not
5313  * available, this will reload it from disk or otherwise do whatever is
5314  * required to make it available, if possible.
5315  *
5316  * Assumes the lock is already held. The lock will be held during the
5317  * duration of this operation.
5318  */
5319 void Texture::
5320 do_reload_ram_image(CData *cdata, bool allow_compression) {
5322  PT(BamCacheRecord) record;
5323 
5324  if (!do_has_compression(cdata)) {
5325  allow_compression = false;
5326  }
5327 
5328  if ((cache->get_cache_textures() || (allow_compression && cache->get_cache_compressed_textures())) && !textures_header_only) {
5329  // See if the texture can be found in the on-disk cache, if it is active.
5330 
5331  record = cache->lookup(cdata->_fullpath, "txo");
5332  if (record != nullptr &&
5333  record->has_data()) {
5334  PT(Texture) tex = DCAST(Texture, record->get_data());
5335 
5336  // But don't use the cache record if the config parameters have changed,
5337  // and we want a different-sized texture now.
5338  int x_size = cdata->_orig_file_x_size;
5339  int y_size = cdata->_orig_file_y_size;
5340  do_adjust_this_size(cdata, x_size, y_size, cdata->_filename.get_basename(), true);
5341  if (x_size != tex->get_x_size() || y_size != tex->get_y_size()) {
5342  if (gobj_cat.is_debug()) {
5343  gobj_cat.debug()
5344  << "Cached texture " << *this << " has size "
5345  << tex->get_x_size() << " x " << tex->get_y_size()
5346  << " instead of " << x_size << " x " << y_size
5347  << "; ignoring cache.\n";
5348  }
5349  } else {
5350  // Also don't keep the cached version if it's compressed but we want
5351  // uncompressed.
5352  if (!allow_compression && tex->get_ram_image_compression() != Texture::CM_off) {
5353  if (gobj_cat.is_debug()) {
5354  gobj_cat.debug()
5355  << "Cached texture " << *this
5356  << " is compressed in cache; ignoring cache.\n";
5357  }
5358  } else {
5359  gobj_cat.info()
5360  << "Texture " << get_name() << " reloaded from disk cache\n";
5361  // We don't want to replace all the texture parameters--for
5362  // instance, we don't want to change the filter type or the border
5363  // color or anything--we just want to get the image and necessary
5364  // associated parameters.
5365  CDReader cdata_tex(tex->_cycler);
5366  cdata->_x_size = cdata_tex->_x_size;
5367  cdata->_y_size = cdata_tex->_y_size;
5368  if (cdata->_num_components != cdata_tex->_num_components) {
5369  cdata->_num_components = cdata_tex->_num_components;
5370  cdata->_format = cdata_tex->_format;
5371  }
5372  cdata->_component_type = cdata_tex->_component_type;
5373  cdata->_compression = cdata_tex->_compression;
5374  cdata->_ram_image_compression = cdata_tex->_ram_image_compression;
5375  cdata->_ram_images = cdata_tex->_ram_images;
5376  cdata->_loaded_from_image = true;
5377 
5378  bool was_compressed = (cdata->_ram_image_compression != CM_off);
5379  if (do_consider_auto_process_ram_image(cdata, uses_mipmaps(), allow_compression)) {
5380  bool is_compressed = (cdata->_ram_image_compression != CM_off);
5381  if (!was_compressed && is_compressed &&
5382  cache->get_cache_compressed_textures()) {
5383  // We've re-compressed the image after loading it from the
5384  // cache. To keep the cache current, rewrite it to the cache
5385  // now, in its newly compressed form.
5386  record->set_data(this, this);
5387  cache->store(record);
5388  }
5389  }
5390 
5391  return;
5392  }
5393  }
5394  }
5395  }
5396 
5397  gobj_cat.info()
5398  << "Reloading texture " << get_name() << "\n";
5399 
5400  int z = 0;
5401  int n = 0;
5402 
5403  if (cdata->_has_read_pages) {
5404  z = cdata->_z_size;
5405  }
5406  if (cdata->_has_read_mipmaps) {
5407  n = cdata->_num_mipmap_levels_read;
5408  }
5409 
5410  cdata->_loaded_from_image = false;
5411  Format orig_format = cdata->_format;
5412  int orig_num_components = cdata->_num_components;
5413 
5414  LoaderOptions options;
5415  if (allow_compression) {
5416  options.set_texture_flags(LoaderOptions::TF_preload |
5417  LoaderOptions::TF_allow_compression);
5418  } else {
5419  options.set_texture_flags(LoaderOptions::TF_preload);
5420  }
5421  do_read(cdata, cdata->_fullpath, cdata->_alpha_fullpath,
5422  cdata->_primary_file_num_channels, cdata->_alpha_file_channel,
5423  z, n, cdata->_has_read_pages, cdata->_has_read_mipmaps, options, nullptr);
5424 
5425  if (orig_num_components == cdata->_num_components) {
5426  // Restore the original format, in case it was needlessly changed during
5427  // the reload operation.
5428  cdata->_format = orig_format;
5429  }
5430 
5431  if (do_has_ram_image(cdata) && record != nullptr) {
5432  if (cache->get_cache_textures() || (cdata->_ram_image_compression != CM_off && cache->get_cache_compressed_textures())) {
5433  // Update the cache.
5434  if (record != nullptr) {
5435  record->add_dependent_file(cdata->_fullpath);
5436  }
5437  record->set_data(this, this);
5438  cache->store(record);
5439  }
5440  }
5441 }
5442 
5443 /**
5444  * This is called internally to uniquify the ram image pointer without
5445  * updating cdata->_image_modified.
5446  */
5447 PTA_uchar Texture::
5448 do_modify_ram_image(CData *cdata) {
5449  if (cdata->_ram_images.empty() || cdata->_ram_images[0]._image.empty() ||
5450  cdata->_ram_image_compression != CM_off) {
5451  do_make_ram_image(cdata);
5452  } else {
5453  do_clear_ram_mipmap_images(cdata);
5454  }
5455  return cdata->_ram_images[0]._image;
5456 }
5457 
5458 /**
5459  * This is called internally to make a new ram image without updating
5460  * cdata->_image_modified.
5461  */
5462 PTA_uchar Texture::
5463 do_make_ram_image(CData *cdata) {
5464  int image_size = do_get_expected_ram_image_size(cdata);
5465  cdata->_ram_images.clear();
5466  cdata->_ram_images.push_back(RamImage());
5467  cdata->_ram_images[0]._page_size = do_get_expected_ram_page_size(cdata);
5468  cdata->_ram_images[0]._image = PTA_uchar::empty_array(image_size, get_class_type());
5469  cdata->_ram_images[0]._pointer_image = nullptr;
5470  cdata->_ram_image_compression = CM_off;
5471 
5472  if (cdata->_has_clear_color) {
5473  // Fill the image with the clear color.
5474  unsigned char pixel[16];
5475  const int pixel_size = do_get_clear_data(cdata, pixel);
5476  nassertr(pixel_size > 0, cdata->_ram_images[0]._image);
5477 
5478  unsigned char *image_data = cdata->_ram_images[0]._image;
5479  for (int i = 0; i < image_size; i += pixel_size) {
5480  memcpy(image_data + i, pixel, pixel_size);
5481  }
5482  }
5483 
5484  return cdata->_ram_images[0]._image;
5485 }
5486 
5487 /**
5488  * Replaces the current system-RAM image with the new data. If compression is
5489  * not CM_off, it indicates that the new data is already pre-compressed in the
5490  * indicated format.
5491  *
5492  * This does *not* affect keep_ram_image.
5493  */
5494 void Texture::
5495 do_set_ram_image(CData *cdata, CPTA_uchar image, Texture::CompressionMode compression,
5496  size_t page_size) {
5497  nassertv(compression != CM_default);
5498  nassertv(compression != CM_off || image.size() == do_get_expected_ram_image_size(cdata));
5499  if (cdata->_ram_images.empty()) {
5500  cdata->_ram_images.push_back(RamImage());
5501  } else {
5502  do_clear_ram_mipmap_images(cdata);
5503  }
5504  if (page_size == 0) {
5505  page_size = image.size();
5506  }
5507  if (cdata->_ram_images[0]._image != image ||
5508  cdata->_ram_images[0]._page_size != page_size ||
5509  cdata->_ram_image_compression != compression) {
5510  cdata->_ram_images[0]._image = image.cast_non_const();
5511  cdata->_ram_images[0]._page_size = page_size;
5512  cdata->_ram_images[0]._pointer_image = nullptr;
5513  cdata->_ram_image_compression = compression;
5514  cdata->inc_image_modified();
5515  }
5516 }
5517 
5518 /**
5519  * This is called internally to uniquify the nth mipmap image pointer without
5520  * updating cdata->_image_modified.
5521  */
5522 PTA_uchar Texture::
5523 do_modify_ram_mipmap_image(CData *cdata, int n) {
5524  nassertr(cdata->_ram_image_compression == CM_off, PTA_uchar());
5525 
5526  if (n >= (int)cdata->_ram_images.size() ||
5527  cdata->_ram_images[n]._image.empty()) {
5528  do_make_ram_mipmap_image(cdata, n);
5529  }
5530  return cdata->_ram_images[n]._image;
5531 }
5532 
5533 /**
5534  *
5535  */
5536 PTA_uchar Texture::
5537 do_make_ram_mipmap_image(CData *cdata, int n) {
5538  nassertr(cdata->_ram_image_compression == CM_off, PTA_uchar(get_class_type()));
5539 
5540  while (n >= (int)cdata->_ram_images.size()) {
5541  cdata->_ram_images.push_back(RamImage());
5542  }
5543 
5544  size_t image_size = do_get_expected_ram_mipmap_image_size(cdata, n);
5545  cdata->_ram_images[n]._image = PTA_uchar::empty_array(image_size, get_class_type());
5546  cdata->_ram_images[n]._pointer_image = nullptr;
5547  cdata->_ram_images[n]._page_size = do_get_expected_ram_mipmap_page_size(cdata, n);
5548 
5549  if (cdata->_has_clear_color) {
5550  // Fill the image with the clear color.
5551  unsigned char pixel[16];
5552  const size_t pixel_size = (size_t)do_get_clear_data(cdata, pixel);
5553  nassertr(pixel_size > 0, cdata->_ram_images[n]._image);
5554 
5555  unsigned char *image_data = cdata->_ram_images[n]._image;
5556  for (size_t i = 0; i < image_size; i += pixel_size) {
5557  memcpy(image_data + i, pixel, pixel_size);
5558  }
5559  }
5560 
5561  return cdata->_ram_images[n]._image;
5562 }
5563 
5564 /**
5565  *
5566  */
5567 void Texture::
5568 do_set_ram_mipmap_image(CData *cdata, int n, CPTA_uchar image, size_t page_size) {
5569  nassertv(cdata->_ram_image_compression != CM_off || image.size() == do_get_expected_ram_mipmap_image_size(cdata, n));
5570 
5571  while (n >= (int)cdata->_ram_images.size()) {
5572  cdata->_ram_images.push_back(RamImage());
5573  }
5574  if (page_size == 0) {
5575  page_size = image.size();
5576  }
5577 
5578  if (cdata->_ram_images[n]._image != image ||
5579  cdata->_ram_images[n]._page_size != page_size) {
5580  cdata->_ram_images[n]._image = image.cast_non_const();
5581  cdata->_ram_images[n]._pointer_image = nullptr;
5582  cdata->_ram_images[n]._page_size = page_size;
5583  cdata->inc_image_modified();
5584  }
5585 }
5586 
5587 /**
5588  * Returns a string with a single pixel representing the clear color of the
5589  * texture in the format of this texture.
5590  *
5591  * In other words, to create an uncompressed RAM texture filled with the clear
5592  * color, it should be initialized with this string repeated for every pixel.
5593  */
5594 size_t Texture::
5595 do_get_clear_data(const CData *cdata, unsigned char *into) const {
5596  nassertr(cdata->_has_clear_color, 0);
5597 
5598  int num_components = cdata->_num_components;
5599  nassertr(num_components > 0, 0);
5600  nassertr(num_components <= 4, 0);
5601 
5602  LVecBase4 clear_value = cdata->_clear_color;
5603 
5604  // Swap red and blue components.
5605  if (num_components >= 3) {
5606  std::swap(clear_value[0], clear_value[2]);
5607  }
5608 
5609  switch (cdata->_component_type) {
5610  case T_unsigned_byte:
5611  if (is_srgb(cdata->_format)) {
5612  xel color;
5613  xelval alpha;
5614  encode_sRGB_uchar(clear_value, color, alpha);
5615  switch (num_components) {
5616  case 4: into[3] = (unsigned char)alpha;
5617  case 3: into[2] = (unsigned char)color.b;
5618  case 2: into[1] = (unsigned char)color.g;
5619  case 1: into[0] = (unsigned char)color.r;
5620  }
5621  } else {
5622  LColor scaled = clear_value.fmin(LColor(1)).fmax(LColor::zero());
5623  scaled *= 255;
5624  for (int i = 0; i < num_components; ++i) {
5625  into[i] = (unsigned char)scaled[i];
5626  }
5627  }
5628  break;
5629 
5630  case T_unsigned_short:
5631  {
5632  LColor scaled = clear_value.fmin(LColor(1)).fmax(LColor::zero());
5633  scaled *= 65535;
5634  for (int i = 0; i < num_components; ++i) {
5635  ((unsigned short *)into)[i] = (unsigned short)scaled[i];
5636  }
5637  break;
5638  }
5639 
5640  case T_float:
5641  for (int i = 0; i < num_components; ++i) {
5642  ((float *)into)[i] = clear_value[i];
5643  }
5644  break;
5645 
5646  case T_unsigned_int_24_8:
5647  nassertr(num_components == 1, 0);
5648  *((unsigned int *)into) =
5649  ((unsigned int)(clear_value[0] * 16777215) << 8) +
5650  (unsigned int)max(min(clear_value[1], (PN_stdfloat)255), (PN_stdfloat)0);
5651  break;
5652 
5653  case T_int:
5654  // Note: there are no 32-bit UNORM textures. Therefore, we don't do any
5655  // normalization here, either.
5656  for (int i = 0; i < num_components; ++i) {
5657  ((int *)into)[i] = (int)clear_value[i];
5658  }
5659  break;
5660 
5661  case T_byte:
5662  {
5663  LColor scaled = clear_value.fmin(LColor(1)).fmax(LColor(-1));
5664  scaled *= 127;
5665  for (int i = 0; i < num_components; ++i) {
5666  ((signed char *)into)[i] = (signed char)scaled[i];
5667  }
5668  break;
5669  }
5670 
5671  case T_short:
5672  {
5673  LColor scaled = clear_value.fmin(LColor(1)).fmax(LColor(-1));
5674  scaled *= 32767;
5675  for (int i = 0; i < num_components; ++i) {
5676  ((short *)into)[i] = (short)scaled[i];
5677  }
5678  break;
5679  }
5680 
5681  case T_half_float:
5682  for (int i = 0; i < num_components; ++i) {
5683  union {
5684  uint32_t ui;
5685  float uf;
5686  } v;
5687  v.uf = clear_value[i];
5688  uint16_t sign = ((v.ui & 0x80000000u) >> 16u);
5689  uint32_t mantissa = (v.ui & 0x007fffffu);
5690  uint16_t exponent = (uint16_t)std::min(std::max((int)((v.ui & 0x7f800000u) >> 23u) - 112, 0), 31);
5691  mantissa += (mantissa & 0x00001000u) << 1u;
5692  ((uint16_t *)into)[i] = (uint16_t)(sign | ((exponent << 10u) | (mantissa >> 13u)));
5693  }
5694  break;
5695 
5696  case T_unsigned_int:
5697  // Note: there are no 32-bit UNORM textures. Therefore, we don't do any
5698  // normalization here, either.
5699  for (int i = 0; i < num_components; ++i) {
5700  ((unsigned int *)into)[i] = (unsigned int)clear_value[i];
5701  }
5702  }
5703 
5704  return num_components * cdata->_component_width;
5705 }
5706 
5707 /**
5708  * Should be called after a texture has been loaded into RAM, this considers
5709  * generating mipmaps and/or compressing the RAM image.
5710  *
5711  * Returns true if the image was modified by this operation, false if it
5712  * wasn't.
5713  */
5714 bool Texture::
5715 consider_auto_process_ram_image(bool generate_mipmaps, bool allow_compression) {
5716  CDWriter cdata(_cycler, false);
5717  return do_consider_auto_process_ram_image(cdata, generate_mipmaps, allow_compression);
5718 }
5719 
5720 /**
5721  * Should be called after a texture has been loaded into RAM, this considers
5722  * generating mipmaps and/or compressing the RAM image.
5723  *
5724  * Returns true if the image was modified by this operation, false if it
5725  * wasn't.
5726  */
5727 bool Texture::
5728 do_consider_auto_process_ram_image(CData *cdata, bool generate_mipmaps,
5729  bool allow_compression) {
5730  bool modified = false;
5731 
5732  if (generate_mipmaps && !driver_generate_mipmaps &&
5733  cdata->_ram_images.size() == 1) {
5734  do_generate_ram_mipmap_images(cdata, false);
5735  modified = true;
5736  }
5737 
5738  if (allow_compression && !driver_compress_textures) {
5739  CompressionMode compression = cdata->_compression;
5740  if (compression == CM_default && compressed_textures) {
5741  compression = CM_on;
5742  }
5743  if (compression != CM_off && cdata->_ram_image_compression == CM_off) {
5745  if (do_compress_ram_image(cdata, compression, QL_default, gsg)) {
5746  if (gobj_cat.is_debug()) {
5747  gobj_cat.debug()
5748  << "Compressed " << get_name() << " with "
5749  << cdata->_ram_image_compression << "\n";
5750  }
5751  modified = true;
5752  }
5753  }
5754  }
5755 
5756  return modified;
5757 }
5758 
5759 /**
5760  *
5761  */
5762 bool Texture::
5763 do_compress_ram_image(CData *cdata, Texture::CompressionMode compression,
5764  Texture::QualityLevel quality_level,
5766  nassertr(compression != CM_off, false);
5767 
5768  if (cdata->_ram_images.empty() || cdata->_ram_image_compression != CM_off) {
5769  return false;
5770  }
5771 
5772  if (compression == CM_on) {
5773  // Select an appropriate compression mode automatically.
5774  switch (cdata->_format) {
5775  case Texture::F_rgbm:
5776  case Texture::F_rgb:
5777  case Texture::F_rgb5:
5778  case Texture::F_rgba5:
5779  case Texture::F_rgb8:
5780  case Texture::F_rgb12:
5781  case Texture::F_rgb332:
5782  case Texture::F_rgb16:
5783  case Texture::F_rgb32:
5784  case Texture::F_rgb10_a2:
5785  if (gsg == nullptr || gsg->get_supports_compressed_texture_format(CM_dxt1)) {
5786  compression = CM_dxt1;
5787  } else if (gsg->get_supports_compressed_texture_format(CM_dxt3)) {
5788  compression = CM_dxt3;
5789  } else if (gsg->get_supports_compressed_texture_format(CM_dxt5)) {
5790  compression = CM_dxt5;
5791  } else if (gsg->get_supports_compressed_texture_format(CM_etc2)) {
5792  compression = CM_etc2;
5793  } else if (gsg->get_supports_compressed_texture_format(CM_etc1)) {
5794  compression = CM_etc1;
5795  }
5796  break;
5797 
5798  case Texture::F_rgba4:
5799  if (gsg == nullptr || gsg->get_supports_compressed_texture_format(CM_dxt3)) {
5800  compression = CM_dxt3;
5801  } else if (gsg->get_supports_compressed_texture_format(CM_dxt5)) {
5802  compression = CM_dxt5;
5803  } else if (gsg->get_supports_compressed_texture_format(CM_etc2)) {
5804  compression = CM_etc2;
5805  }
5806  break;
5807 
5808  case Texture::F_rgba:
5809  case Texture::F_rgba8:
5810  case Texture::F_rgba12:
5811  case Texture::F_rgba16:
5812  case Texture::F_rgba32:
5813  if (gsg == nullptr || gsg->get_supports_compressed_texture_format(CM_dxt5)) {
5814  compression = CM_dxt5;
5815  } else if (gsg->get_supports_compressed_texture_format(CM_etc2)) {
5816  compression = CM_etc2;
5817  }
5818  break;
5819 
5820  case Texture::F_red:
5821  case Texture::F_rg:
5822  if (gsg == nullptr || gsg->get_supports_compressed_texture_format(CM_rgtc)) {
5823  compression = CM_rgtc;
5824  } else if (gsg->get_supports_compressed_texture_format(CM_eac)) {
5825  compression = CM_eac;
5826  }
5827  break;
5828 
5829  default:
5830  break;
5831  }
5832  }
5833 
5834  // Choose an appropriate quality level.
5835  if (quality_level == Texture::QL_default) {
5836  quality_level = cdata->_quality_level;
5837  }
5838  if (quality_level == Texture::QL_default) {
5839  quality_level = texture_quality_level;
5840  }
5841 
5842  if (compression == CM_rgtc) {
5843  // We should compress RGTC ourselves, as squish does not support it.
5844  if (cdata->_component_type != T_unsigned_byte) {
5845  return false;
5846  }
5847 
5848  if (!do_has_all_ram_mipmap_images(cdata)) {
5849  // If we're about to compress the RAM image, we should ensure that we
5850  // have all of the mipmap levels first.
5851  do_generate_ram_mipmap_images(cdata, false);
5852  }
5853 
5854  RamImages compressed_ram_images;
5855  compressed_ram_images.resize(cdata->_ram_images.size());
5856 
5857  for (size_t n = 0; n < cdata->_ram_images.size(); ++n) {
5858  const RamImage *uncompressed_image = &cdata->_ram_images[n];
5859 
5860  int x_size = do_get_expected_mipmap_x_size(cdata, n);
5861  int y_size = do_get_expected_mipmap_y_size(cdata, n);
5862  int num_pages = do_get_expected_mipmap_num_pages(cdata, n);
5863 
5864  // It is important that we handle image sizes that aren't a multiple of
5865  // the block size, since this method may be used to compress mipmaps,
5866  // which go all the way to 1x1. Pad the image if necessary.
5867  RamImage temp_image;
5868  if ((x_size | y_size) & 0x3) {
5869  int virtual_x_size = x_size;
5870  int virtual_y_size = y_size;
5871  x_size = (x_size + 3) & ~0x3;
5872  y_size = (y_size + 3) & ~0x3;
5873 
5874  temp_image._page_size = x_size * y_size * cdata->_num_components;
5875  temp_image._image = PTA_uchar::empty_array(temp_image._page_size * num_pages);
5876 
5877  for (int z = 0; z < num_pages; ++z) {
5878  unsigned char *dest = temp_image._image.p() + z * temp_image._page_size;
5879  unsigned const char *src = uncompressed_image->_image.p() + z * uncompressed_image->_page_size;
5880 
5881  for (int y = 0; y < virtual_y_size; ++y) {
5882  memcpy(dest, src, virtual_x_size);
5883  src += virtual_x_size;
5884  dest += x_size;
5885  }
5886  }
5887 
5888  uncompressed_image = &temp_image;
5889  }
5890 
5891  // Create a new image to hold the compressed texture pages.
5892  RamImage &compressed_image = compressed_ram_images[n];
5893  compressed_image._page_size = (x_size * y_size * cdata->_num_components) >> 1;
5894  compressed_image._image = PTA_uchar::empty_array(compressed_image._page_size * num_pages);
5895 
5896  if (cdata->_num_components == 1) {
5897  do_compress_ram_image_bc4(*uncompressed_image, compressed_image,
5898  x_size, y_size, num_pages);
5899  } else if (cdata->_num_components == 2) {
5900  do_compress_ram_image_bc5(*uncompressed_image, compressed_image,
5901  x_size, y_size, num_pages);
5902  } else {
5903  // Invalid.
5904  return false;
5905  }
5906  }
5907 
5908  cdata->_ram_images.swap(compressed_ram_images);
5909  cdata->_ram_image_compression = CM_rgtc;
5910  return true;
5911  }
5912 
5913 #ifdef HAVE_SQUISH
5914  if (cdata->_texture_type != TT_3d_texture &&
5915  cdata->_texture_type != TT_2d_texture_array &&
5916  cdata->_component_type == T_unsigned_byte) {
5917  int squish_flags = 0;
5918  switch (compression) {
5919  case CM_dxt1:
5920  squish_flags |= squish::kDxt1;
5921  break;
5922 
5923  case CM_dxt3:
5924  squish_flags |= squish::kDxt3;
5925  break;
5926 
5927  case CM_dxt5:
5928  squish_flags |= squish::kDxt5;
5929  break;
5930 
5931  default:
5932  break;
5933  }
5934 
5935  if (squish_flags != 0) {
5936  // This compression mode is supported by squish; use it.
5937  switch (quality_level) {
5938  case QL_fastest:
5939  squish_flags |= squish::kColourRangeFit;
5940  break;
5941 
5942  case QL_normal:
5943  // ColourClusterFit is just too slow for everyday use.
5944  squish_flags |= squish::kColourRangeFit;
5945  // squish_flags |= squish::kColourClusterFit;
5946  break;
5947 
5948  case QL_best:
5949  squish_flags |= squish::kColourIterativeClusterFit;
5950  break;
5951 
5952  default:
5953  break;
5954  }
5955 
5956  if (do_squish(cdata, compression, squish_flags)) {
5957  return true;
5958  }
5959  }
5960  }
5961 #endif // HAVE_SQUISH
5962 
5963  return false;
5964 }
5965 
5966 /**
5967  *
5968  */
5969 bool Texture::
5970 do_uncompress_ram_image(CData *cdata) {
5971  nassertr(!cdata->_ram_images.empty(), false);
5972 
5973  if (cdata->_ram_image_compression == CM_rgtc) {
5974  // We should decompress RGTC ourselves, as squish doesn't support it.
5975  RamImages uncompressed_ram_images;
5976  uncompressed_ram_images.resize(cdata->_ram_images.size());
5977 
5978  for (size_t n = 0; n < cdata->_ram_images.size(); ++n) {
5979  const RamImage &compressed_image = cdata->_ram_images[n];
5980 
5981  int x_size = do_get_expected_mipmap_x_size(cdata, n);
5982  int y_size = do_get_expected_mipmap_y_size(cdata, n);
5983  int num_pages = do_get_expected_mipmap_num_pages(cdata, n);
5984 
5985  RamImage &uncompressed_image = uncompressed_ram_images[n];
5986  uncompressed_image._page_size = do_get_expected_ram_mipmap_page_size(cdata, n);
5987  uncompressed_image._image = PTA_uchar::empty_array(uncompressed_image._page_size * num_pages);
5988 
5989  if (cdata->_num_components == 1) {
5990  do_uncompress_ram_image_bc4(compressed_image, uncompressed_image,
5991  x_size, y_size, num_pages);
5992  } else if (cdata->_num_components == 2) {
5993  do_uncompress_ram_image_bc5(compressed_image, uncompressed_image,
5994  x_size, y_size, num_pages);
5995  } else {
5996  // Invalid.
5997  return false;
5998  }
5999  }
6000  cdata->_ram_images.swap(uncompressed_ram_images);
6001  cdata->_ram_image_compression = CM_off;
6002  return true;
6003  }
6004 
6005 #ifdef HAVE_SQUISH
6006  if (cdata->_texture_type != TT_3d_texture &&
6007  cdata->_texture_type != TT_2d_texture_array &&
6008  cdata->_component_type == T_unsigned_byte) {
6009  int squish_flags = 0;
6010  switch (cdata->_ram_image_compression) {
6011  case CM_dxt1:
6012  squish_flags |= squish::kDxt1;
6013  break;
6014 
6015  case CM_dxt3:
6016  squish_flags |= squish::kDxt3;
6017  break;
6018 
6019  case CM_dxt5:
6020  squish_flags |= squish::kDxt5;
6021  break;
6022 
6023  default:
6024  break;
6025  }
6026 
6027  if (squish_flags != 0) {
6028  // This compression mode is supported by squish; use it.
6029  if (do_unsquish(cdata, squish_flags)) {
6030  return true;
6031  }
6032  }
6033  }
6034 #endif // HAVE_SQUISH
6035  return false;
6036 }
6037 
6038 /**
6039  * Compresses a RAM image using BC4 compression.
6040  */
6041 void Texture::
6042 do_compress_ram_image_bc4(const RamImage &uncompressed_image,
6043  RamImage &compressed_image,
6044  int x_size, int y_size, int num_pages) {
6045  int x_blocks = (x_size >> 2);
6046  int y_blocks = (y_size >> 2);
6047 
6048  // NB. This algorithm isn't fully optimal, since it doesn't try to make use
6049  // of the secondary interpolation mode supported by BC4. This is not
6050  // important for most textures, but it may be added in the future.
6051 
6052  nassertv((size_t)x_blocks * (size_t)y_blocks * 4 * 4 <= uncompressed_image._page_size);
6053  nassertv((size_t)x_size * (size_t)y_size == uncompressed_image._page_size);
6054 
6055  static const int remap[] = {1, 7, 6, 5, 4, 3, 2, 0};
6056 
6057  for (int z = 0; z < num_pages; ++z) {
6058  unsigned char *dest = compressed_image._image.p() + z * compressed_image._page_size;
6059  unsigned const char *src = uncompressed_image._image.p() + z * uncompressed_image._page_size;
6060 
6061  // Convert one 4 x 4 block at a time.
6062  for (int y = 0; y < y_blocks; ++y) {
6063  for (int x = 0; x < x_blocks; ++x) {
6064  int a, b, c, d;
6065  float fac, add;
6066  unsigned char minv, maxv;
6067  unsigned const char *blk = src;
6068 
6069  // Find the minimum and maximum value in the block.
6070  minv = blk[0];
6071  maxv = blk[0];
6072  minv = min(blk[1], minv); maxv = max(blk[1], maxv);
6073  minv = min(blk[2], minv); maxv = max(blk[2], maxv);
6074  minv = min(blk[3], minv); maxv = max(blk[3], maxv);
6075  blk += x_size;
6076  minv = min(blk[0], minv); maxv = max(blk[0], maxv);
6077  minv = min(blk[1], minv); maxv = max(blk[1], maxv);
6078  minv = min(blk[2], minv); maxv = max(blk[2], maxv);
6079  minv = min(blk[3], minv); maxv = max(blk[3], maxv);
6080  blk += x_size;
6081  minv = min(blk[0], minv); maxv = max(blk[0], maxv);
6082  minv = min(blk[1], minv); maxv = max(blk[1], maxv);
6083  minv = min(blk[2], minv); maxv = max(blk[2], maxv);
6084  minv = min(blk[3], minv); maxv = max(blk[3], maxv);
6085  blk += x_size;
6086  minv = min(blk[0], minv); maxv = max(blk[0], maxv);
6087  minv = min(blk[1], minv); maxv = max(blk[1], maxv);
6088  minv = min(blk[2], minv); maxv = max(blk[2], maxv);
6089  minv = min(blk[3], minv); maxv = max(blk[3], maxv);
6090 
6091  // Now calculate the index for each pixel.
6092  blk = src;
6093  if (maxv > minv) {
6094  fac = 7.5f / (maxv - minv);
6095  } else {
6096  fac = 0;
6097  }
6098  add = -minv * fac;
6099  a = (remap[(int)(blk[0] * fac + add)])
6100  | (remap[(int)(blk[1] * fac + add)] << 3)
6101  | (remap[(int)(blk[2] * fac + add)] << 6)
6102  | (remap[(int)(blk[3] * fac + add)] << 9);
6103  blk += x_size;
6104  b = (remap[(int)(blk[0] * fac + add)] << 4)
6105  | (remap[(int)(blk[1] * fac + add)] << 7)
6106  | (remap[(int)(blk[2] * fac + add)] << 10)
6107  | (remap[(int)(blk[3] * fac + add)] << 13);
6108  blk += x_size;
6109  c = (remap[(int)(blk[0] * fac + add)])
6110  | (remap[(int)(blk[1] * fac + add)] << 3)
6111  | (remap[(int)(blk[2] * fac + add)] << 6)
6112  | (remap[(int)(blk[3] * fac + add)] << 9);
6113  blk += x_size;
6114  d = (remap[(int)(blk[0] * fac + add)] << 4)
6115  | (remap[(int)(blk[1] * fac + add)] << 7)
6116  | (remap[(int)(blk[2] * fac + add)] << 10)
6117  | (remap[(int)(blk[3] * fac + add)] << 13);
6118 
6119  *(dest++) = maxv;
6120  *(dest++) = minv;
6121  *(dest++) = a & 0xff;
6122  *(dest++) = (a >> 8) | (b & 0xf0);
6123  *(dest++) = b >> 8;
6124  *(dest++) = c & 0xff;
6125  *(dest++) = (c >> 8) | (d & 0xf0);
6126  *(dest++) = d >> 8;
6127 
6128  // Advance to the beginning of the next 4x4 block.
6129  src += 4;
6130  }
6131  src += x_size * 3;
6132  }
6134  }
6135 }
6136 
6137 /**
6138  * Compresses a RAM image using BC5 compression.
6139  */
6140 void Texture::
6141 do_compress_ram_image_bc5(const RamImage &uncompressed_image,
6142  RamImage &compressed_image,
6143  int x_size, int y_size, int num_pages) {
6144  int x_blocks = (x_size >> 2);
6145  int y_blocks = (y_size >> 2);
6146  int stride = x_size * 2;
6147 
6148  // BC5 uses the same compression algorithm as BC4, except repeated for two
6149  // channels.
6150 
6151  nassertv((size_t)x_blocks * (size_t)y_blocks * 4 * 4 * 2 <= uncompressed_image._page_size);
6152  nassertv((size_t)stride * (size_t)y_size == uncompressed_image._page_size);
6153 
6154  static const int remap[] = {1, 7, 6, 5, 4, 3, 2, 0};
6155 
6156  for (int z = 0; z < num_pages; ++z) {
6157  unsigned char *dest = compressed_image._image.p() + z * compressed_image._page_size;
6158  unsigned const char *src = uncompressed_image._image.p() + z * uncompressed_image._page_size;
6159 
6160  // Convert one 4 x 4 block at a time.
6161  for (int y = 0; y < y_blocks; ++y) {
6162  for (int x = 0; x < x_blocks; ++x) {
6163  int a, b, c, d;
6164  float fac, add;
6165  unsigned char minv, maxv;
6166  unsigned const char *blk = src;
6167 
6168  // Find the minimum and maximum red value in the block.
6169  minv = blk[0];
6170  maxv = blk[0];
6171  minv = min(blk[2], minv); maxv = max(blk[2], maxv);
6172  minv = min(blk[4], minv); maxv = max(blk[4], maxv);
6173  minv = min(blk[6], minv); maxv = max(blk[6], maxv);
6174  blk += stride;
6175  minv = min(blk[0], minv); maxv = max(blk[0], maxv);
6176  minv = min(blk[2], minv); maxv = max(blk[2], maxv);
6177  minv = min(blk[4], minv); maxv = max(blk[4], maxv);
6178  minv = min(blk[6], minv); maxv = max(blk[6], maxv);
6179  blk += stride;
6180  minv = min(blk[0], minv); maxv = max(blk[0], maxv);
6181  minv = min(blk[2], minv); maxv = max(blk[2], maxv);
6182  minv = min(blk[4], minv); maxv = max(blk[4], maxv);
6183  minv = min(blk[6], minv); maxv = max(blk[6], maxv);
6184  blk += stride;
6185  minv = min(blk[0], minv); maxv = max(blk[0], maxv);
6186  minv = min(blk[2], minv); maxv = max(blk[2], maxv);
6187  minv = min(blk[4], minv); maxv = max(blk[4], maxv);
6188  minv = min(blk[6], minv); maxv = max(blk[6], maxv);
6189 
6190  // Now calculate the index for each pixel.
6191  if (maxv > minv) {
6192  fac = 7.5f / (maxv - minv);
6193  } else {
6194  fac = 0;
6195  }
6196  add = -minv * fac;
6197  blk = src;
6198  a = (remap[(int)(blk[0] * fac + add)])
6199  | (remap[(int)(blk[2] * fac + add)] << 3)
6200  | (remap[(int)(blk[4] * fac + add)] << 6)
6201  | (remap[(int)(blk[6] * fac + add)] << 9);
6202  blk += stride;
6203  b = (remap[(int)(blk[0] * fac + add)] << 4)
6204  | (remap[(int)(blk[2] * fac + add)] << 7)
6205  | (remap[(int)(blk[4] * fac + add)] << 10)
6206  | (remap[(int)(blk[6] * fac + add)] << 13);
6207  blk += stride;
6208  c = (remap[(int)(blk[0] * fac + add)])
6209  | (remap[(int)(blk[2] * fac + add)] << 3)
6210  | (remap[(int)(blk[4] * fac + add)] << 6)
6211  | (remap[(int)(blk[6] * fac + add)] << 9);
6212  blk += stride;
6213  d = (remap[(int)(blk[0] * fac + add)] << 4)
6214  | (remap[(int)(blk[2] * fac + add)] << 7)
6215  | (remap[(int)(blk[4] * fac + add)] << 10)
6216  | (remap[(int)(blk[6] * fac + add)] << 13);
6217 
6218  *(dest++) = maxv;
6219  *(dest++) = minv;
6220  *(dest++) = a & 0xff;
6221  *(dest++) = (a >> 8) | (b & 0xf0);
6222  *(dest++) = b >> 8;
6223  *(dest++) = c & 0xff;
6224  *(dest++) = (c >> 8) | (d & 0xf0);
6225  *(dest++) = d >> 8;
6226 
6227  // Find the minimum and maximum green value in the block.
6228  blk = src + 1;
6229  minv = blk[0];
6230  maxv = blk[0];
6231  minv = min(blk[2], minv); maxv = max(blk[2], maxv);
6232  minv = min(blk[4], minv); maxv = max(blk[4], maxv);
6233  minv = min(blk[6], minv); maxv = max(blk[6], maxv);
6234  blk += stride;
6235  minv = min(blk[0], minv); maxv = max(blk[0], maxv);
6236  minv = min(blk[2], minv); maxv = max(blk[2], maxv);
6237  minv = min(blk[4], minv); maxv = max(blk[4], maxv);
6238  minv = min(blk[6], minv); maxv = max(blk[6], maxv);
6239  blk += stride;
6240  minv = min(blk[0], minv); maxv = max(blk[0], maxv);
6241  minv = min(blk[2], minv); maxv = max(blk[2], maxv);
6242  minv = min(blk[4], minv); maxv = max(blk[4], maxv);
6243  minv = min(blk[6], minv); maxv = max(blk[6], maxv);
6244  blk += stride;
6245  minv = min(blk[0], minv); maxv = max(blk[0], maxv);
6246  minv = min(blk[2], minv); maxv = max(blk[2], maxv);
6247  minv = min(blk[4], minv); maxv = max(blk[4], maxv);
6248  minv = min(blk[6], minv); maxv = max(blk[6], maxv);
6249 
6250  // Now calculate the index for each pixel.
6251  if (maxv > minv) {
6252  fac = 7.5f / (maxv - minv);
6253  } else {
6254  fac = 0;
6255  }
6256  add = -minv * fac;
6257  blk = src + 1;
6258  a = (remap[(int)(blk[0] * fac + add)])
6259  | (remap[(int)(blk[2] * fac + add)] << 3)
6260  | (remap[(int)(blk[4] * fac + add)] << 6)
6261  | (remap[(int)(blk[6] * fac + add)] << 9);
6262  blk += stride;
6263  b = (remap[(int)(blk[0] * fac + add)] << 4)
6264  | (remap[(int)(blk[2] * fac + add)] << 7)
6265  | (remap[(int)(blk[4] * fac + add)] << 10)
6266  | (remap[(int)(blk[6] * fac + add)] << 13);
6267  blk += stride;
6268  c = (remap[(int)(blk[0] * fac + add)])
6269  | (remap[(int)(blk[2] * fac + add)] << 3)
6270  | (remap[(int)(blk[4] * fac + add)] << 6)
6271  | (remap[(int)(blk[6] * fac + add)] << 9);
6272  blk += stride;
6273  d = (remap[(int)(blk[0] * fac + add)] << 4)
6274  | (remap[(int)(blk[2] * fac + add)] << 7)
6275  | (remap[(int)(blk[4] * fac + add)] << 10)
6276  | (remap[(int)(blk[6] * fac + add)] << 13);
6277 
6278  *(dest++) = maxv;
6279  *(dest++) = minv;
6280  *(dest++) = a & 0xff;
6281  *(dest++) = (a >> 8) | (b & 0xf0);
6282  *(dest++) = b >> 8;
6283  *(dest++) = c & 0xff;
6284  *(dest++) = (c >> 8) | (d & 0xf0);
6285  *(dest++) = d >> 8;
6286 
6287  // Advance to the beginning of the next 4x4 block.
6288  src += 8;
6289  }
6290  src += stride * 3;
6291  }
6293  }
6294 }
6295 
6296 /**
6297  * Decompresses a RAM image compressed using BC4.
6298  */
6299 void Texture::
6300 do_uncompress_ram_image_bc4(const RamImage &compressed_image,
6301  RamImage &uncompressed_image,
6302  int x_size, int y_size, int num_pages) {
6303  int x_blocks = (x_size >> 2);
6304  int y_blocks = (y_size >> 2);
6305 
6306  for (int z = 0; z < num_pages; ++z) {
6307  unsigned char *dest = uncompressed_image._image.p() + z * uncompressed_image._page_size;
6308  unsigned const char *src = compressed_image._image.p() + z * compressed_image._page_size;
6309 
6310  // Unconvert one 4 x 4 block at a time.
6311  uint8_t tbl[8];
6312  for (int y = 0; y < y_blocks; ++y) {
6313  for (int x = 0; x < x_blocks; ++x) {
6314  unsigned char *blk = dest;
6315  tbl[0] = src[0];
6316  tbl[1] = src[1];
6317  if (tbl[0] > tbl[1]) {
6318  tbl[2] = (tbl[0] * 6 + tbl[1] * 1) / 7.0f;
6319  tbl[3] = (tbl[0] * 5 + tbl[1] * 2) / 7.0f;
6320  tbl[4] = (tbl[0] * 4 + tbl[1] * 3) / 7.0f;
6321  tbl[5] = (tbl[0] * 3 + tbl[1] * 4) / 7.0f;
6322  tbl[6] = (tbl[0] * 2 + tbl[1] * 5) / 7.0f;
6323  tbl[7] = (tbl[0] * 1 + tbl[1] * 6) / 7.0f;
6324  } else {
6325  tbl[2] = (tbl[0] * 4 + tbl[1] * 1) / 5.0f;
6326  tbl[3] = (tbl[0] * 3 + tbl[1] * 2) / 5.0f;
6327  tbl[4] = (tbl[0] * 2 + tbl[1] * 3) / 5.0f;
6328  tbl[5] = (tbl[0] * 1 + tbl[1] * 4) / 5.0f;
6329  tbl[6] = 0;
6330  tbl[7] = 255;
6331  }
6332  int v = src[2] + (src[3] << 8) + (src[4] << 16);
6333  blk[0] = tbl[v & 0x7];
6334  blk[1] = tbl[(v & 0x000038) >> 3];
6335  blk[2] = tbl[(v & 0x0001c0) >> 6];
6336  blk[3] = tbl[(v & 0x000e00) >> 9];
6337  blk += x_size;
6338  blk[0] = tbl[(v & 0x007000) >> 12];
6339  blk[1] = tbl[(v & 0x038000) >> 15];
6340  blk[2] = tbl[(v & 0x1c0000) >> 18];
6341  blk[3] = tbl[(v & 0xe00000) >> 21];
6342  blk += x_size;
6343  v = src[5] + (src[6] << 8) + (src[7] << 16);
6344  blk[0] = tbl[v & 0x7];
6345  blk[1] = tbl[(v & 0x000038) >> 3];
6346  blk[2] = tbl[(v & 0x0001c0) >> 6];
6347  blk[3] = tbl[(v & 0x000e00) >> 9];
6348  blk += x_size;
6349  blk[0] = tbl[(v & 0x007000) >> 12];
6350  blk[1] = tbl[(v & 0x038000) >> 15];
6351  blk[2] = tbl[(v & 0x1c0000) >> 18];
6352  blk[3] = tbl[(v & 0xe00000) >> 21];
6353  src += 8;
6354  dest += 4;
6355  }
6356  dest += x_size * 3;
6357  }
6359  }
6360 }
6361 
6362 /**
6363  * Decompresses a RAM image compressed using BC5.
6364  */
6365 void Texture::
6366 do_uncompress_ram_image_bc5(const RamImage &compressed_image,
6367  RamImage &uncompressed_image,
6368  int x_size, int y_size, int num_pages) {
6369  int x_blocks = (x_size >> 2);
6370  int y_blocks = (y_size >> 2);
6371  int stride = x_size * 2;
6372 
6373  for (int z = 0; z < num_pages; ++z) {
6374  unsigned char *dest = uncompressed_image._image.p() + z * uncompressed_image._page_size;
6375  unsigned const char *src = compressed_image._image.p() + z * compressed_image._page_size;
6376 
6377  // Unconvert one 4 x 4 block at a time.
6378  uint8_t red[8];
6379  uint8_t grn[8];
6380  for (int y = 0; y < y_blocks; ++y) {
6381  for (int x = 0; x < x_blocks; ++x) {
6382  unsigned char *blk = dest;
6383  red[0] = src[0];
6384  red[1] = src[1];
6385  if (red[0] > red[1]) {
6386  red[2] = (red[0] * 6 + red[1] * 1) / 7.0f;
6387  red[3] = (red[0] * 5 + red[1] * 2) / 7.0f;
6388  red[4] = (red[0] * 4 + red[1] * 3) / 7.0f;
6389  red[5] = (red[0] * 3 + red[1] * 4) / 7.0f;
6390  red[6] = (red[0] * 2 + red[1] * 5) / 7.0f;
6391  red[7] = (red[0] * 1 + red[1] * 6) / 7.0f;
6392  } else {
6393  red[2] = (red[0] * 4 + red[1] * 1) / 5.0f;
6394  red[3] = (red[0] * 3 + red[1] * 2) / 5.0f;
6395  red[4] = (red[0] * 2 + red[1] * 3) / 5.0f;
6396  red[5] = (red[0] * 1 + red[1] * 4) / 5.0f;
6397  red[6] = 0;
6398  red[7] = 255;
6399  }
6400  grn[0] = src[8];
6401  grn[1] = src[9];
6402  if (grn[0] > grn[1]) {
6403  grn[2] = (grn[0] * 6 + grn[1] * 1) / 7.0f;
6404  grn[3] = (grn[0] * 5 + grn[1] * 2) / 7.0f;
6405  grn[4] = (grn[0] * 4 + grn[1] * 3) / 7.0f;
6406  grn[5] = (grn[0] * 3 + grn[1] * 4) / 7.0f;
6407  grn[6] = (grn[0] * 2 + grn[1] * 5) / 7.0f;
6408  grn[7] = (grn[0] * 1 + grn[1] * 6) / 7.0f;
6409  } else {
6410  grn[2] = (grn[0] * 4 + grn[1] * 1) / 5.0f;
6411  grn[3] = (grn[0] * 3 + grn[1] * 2) / 5.0f;
6412  grn[4] = (grn[0] * 2 + grn[1] * 3) / 5.0f;
6413  grn[5] = (grn[0] * 1 + grn[1] * 4) / 5.0f;
6414  grn[6] = 0;
6415  grn[7] = 255;
6416  }
6417  int r = src[2] + (src[3] << 8) + (src[4] << 16);
6418  int g = src[10] + (src[11] << 8) + (src[12] << 16);
6419  blk[0] = red[r & 0x7];
6420  blk[1] = grn[g & 0x7];
6421  blk[2] = red[(r & 0x000038) >> 3];
6422  blk[3] = grn[(g & 0x000038) >> 3];
6423  blk[4] = red[(r & 0x0001c0) >> 6];
6424  blk[5] = grn[(g & 0x0001c0) >> 6];
6425  blk[6] = red[(r & 0x000e00) >> 9];
6426  blk[7] = grn[(g & 0x000e00) >> 9];
6427  blk += stride;
6428  blk[0] = red[(r & 0x007000) >> 12];
6429  blk[1] = grn[(g & 0x007000) >> 12];
6430  blk[2] = red[(r & 0x038000) >> 15];
6431  blk[3] = grn[(g & 0x038000) >> 15];
6432  blk[4] = red[(r & 0x1c0000) >> 18];
6433  blk[5] = grn[(g & 0x1c0000) >> 18];
6434  blk[6] = red[(r & 0xe00000) >> 21];
6435  blk[7] = grn[(g & 0xe00000) >> 21];
6436  blk += stride;
6437  r = src[5] + (src[6] << 8) + (src[7] << 16);
6438  g = src[13] + (src[14] << 8) + (src[15] << 16);
6439  blk[0] = red[r & 0x7];
6440  blk[1] = grn[g & 0x7];
6441  blk[2] = red[(r & 0x000038) >> 3];
6442  blk[3] = grn[(g & 0x000038) >> 3];
6443  blk[4] = red[(r & 0x0001c0) >> 6];
6444  blk[5] = grn[(g & 0x0001c0) >> 6];
6445  blk[6] = red[(r & 0x000e00) >> 9];
6446  blk[7] = grn[(g & 0x000e00) >> 9];
6447  blk += stride;
6448  blk[0] = red[(r & 0x007000) >> 12];
6449  blk[1] = grn[(g & 0x007000) >> 12];
6450  blk[2] = red[(r & 0x038000) >> 15];
6451  blk[3] = grn[(g & 0x038000) >> 15];
6452  blk[4] = red[(r & 0x1c0000) >> 18];
6453  blk[5] = grn[(g & 0x1c0000) >> 18];
6454  blk[6] = red[(r & 0xe00000) >> 21];
6455  blk[7] = grn[(g & 0xe00000) >> 21];
6456  src += 16;
6457  dest += 8;
6458  }
6459  dest += stride * 3;
6460  }
6462  }
6463 }
6464 
6465 /**
6466  *
6467  */
6468 bool Texture::
6469 do_has_all_ram_mipmap_images(const CData *cdata) const {
6470  if (cdata->_ram_images.empty() || cdata->_ram_images[0]._image.empty()) {
6471  // If we don't even have a base image, the answer is no.
6472  return false;
6473  }
6474  if (!uses_mipmaps()) {
6475  // If we have a base image and don't require mipmapping, the answer is
6476  // yes.
6477  return true;
6478  }
6479 
6480  // Check that we have enough mipmap levels to meet the size requirements.
6481  int size = max(cdata->_x_size, max(cdata->_y_size, cdata->_z_size));
6482  int n = 0;
6483  int x = 1;
6484  while (x < size) {
6485  x = (x << 1);
6486  ++n;
6487  if (n >= (int)cdata->_ram_images.size() || cdata->_ram_images[n]._image.empty()) {
6488  return false;
6489  }
6490  }
6491 
6492  return true;
6493 }
6494 
6495 /**
6496  * Considers whether the z_size (or num_views) should automatically be
6497  * adjusted when the user loads a new page. Returns true if the z size is
6498  * valid, false otherwise.
6499  *
6500  * Assumes the lock is already held.
6501  */
6502 bool Texture::
6503 do_reconsider_z_size(CData *cdata, int z, const LoaderOptions &options) {
6504  if (z >= cdata->_z_size * cdata->_num_views) {
6505  bool num_views_specified = true;
6506  if (options.get_texture_flags() & LoaderOptions::TF_multiview) {
6507  // This flag is false if is a multiview texture with a specified number
6508  // of views. It is true if it is not a multiview texture, or if it is
6509  // but the number of views is explicitly specified.
6510  num_views_specified = (options.get_texture_num_views() != 0);
6511  }
6512 
6513  if (num_views_specified &&
6514  (cdata->_texture_type == Texture::TT_3d_texture ||
6515  cdata->_texture_type == Texture::TT_2d_texture_array)) {
6516  // If we're loading a page past _z_size, treat it as an implicit request
6517  // to enlarge _z_size. However, this is only legal if this is, in fact,
6518  // a 3-d texture or a 2d texture array (cube maps always have z_size 6,
6519  // and other types have z_size 1).
6520  nassertr(cdata->_num_views != 0, false);
6521  cdata->_z_size = (z / cdata->_num_views) + 1;
6522 
6523  } else if (cdata->_z_size != 0) {
6524  // In the case of a 2-d texture or cube map, or a 3-d texture with an
6525  // unspecified _num_views, assume we're loading views of a multiview
6526  // texture.
6527  cdata->_num_views = (z / cdata->_z_size) + 1;
6528 
6529  } else {
6530  // The first image loaded sets an implicit z-size.
6531  cdata->_z_size = 1;
6532  }
6533 
6534  // Increase the size of the data buffer to make room for the new texture
6535  // level.
6536  do_allocate_pages(cdata);
6537  }
6538 
6539  return true;
6540 }
6541 
6542 /**
6543  * Called internally by do_reconsider_z_size() to allocate new memory in
6544  * _ram_images[0] for the new number of pages.
6545  *
6546  * Assumes the lock is already held.
6547  */
6548 void Texture::
6549 do_allocate_pages(CData *cdata) {
6550  size_t new_size = do_get_expected_ram_image_size(cdata);
6551  if (!cdata->_ram_images.empty() &&
6552  !cdata->_ram_images[0]._image.empty() &&
6553  new_size > cdata->_ram_images[0]._image.size()) {
6554  cdata->_ram_images[0]._image.insert(cdata->_ram_images[0]._image.end(), new_size - cdata->_ram_images[0]._image.size(), 0);
6555  nassertv(cdata->_ram_images[0]._image.size() == new_size);
6556  }
6557 }
6558 
6559 /**
6560  * Resets the internal Texture properties when a new image file is loaded.
6561  * Returns true if the new image is valid, false otherwise.
6562  *
6563  * Assumes the lock is already held.
6564  */
6565 bool Texture::
6566 do_reconsider_image_properties(CData *cdata, int x_size, int y_size, int num_components,
6567  Texture::ComponentType component_type, int z,
6568  const LoaderOptions &options) {
6569  if (!cdata->_loaded_from_image || num_components != cdata->_num_components || component_type != cdata->_component_type) {
6570  // Come up with a default format based on the number of channels. But
6571  // only do this the first time the file is loaded, or if the number of
6572  // channels in the image changes on subsequent loads.
6573 
6574  // TODO: handle sRGB properly
6575  switch (num_components) {
6576  case 1:
6577  cdata->_format = F_luminance;
6578  break;
6579 
6580  case 2:
6581  cdata->_format = F_luminance_alpha;
6582  break;
6583 
6584  case 3:
6585  cdata->_format = F_rgb;
6586  break;
6587 
6588  case 4:
6589  cdata->_format = F_rgba;
6590  break;
6591 
6592  default:
6593  // Eh?
6594  nassert_raise("unexpected channel count");
6595  cdata->_format = F_rgb;
6596  return false;
6597  }
6598  }
6599 
6600  if (!cdata->_loaded_from_image) {
6601  if ((options.get_texture_flags() & LoaderOptions::TF_allow_1d) &&
6602  cdata->_texture_type == TT_2d_texture && x_size != 1 && y_size == 1) {
6603  // If we're loading an Nx1 size texture, infer a 1-d texture type.
6604  cdata->_texture_type = TT_1d_texture;
6605  }
6606 
6607 #ifndef NDEBUG
6608  switch (cdata->_texture_type) {
6609  case TT_1d_texture:
6610  case TT_buffer_texture:
6611  nassertr(y_size == 1, false);
6612  break;
6613  case TT_cube_map:
6614  case TT_cube_map_array:
6615  nassertr(x_size == y_size, false);
6616  break;
6617  default:
6618  break;
6619  }
6620 #endif
6621  if ((cdata->_x_size != x_size)||(cdata->_y_size != y_size)) {
6622  do_set_pad_size(cdata, 0, 0, 0);
6623  }
6624  cdata->_x_size = x_size;
6625  cdata->_y_size = y_size;
6626  cdata->_num_components = num_components;
6627  do_set_component_type(cdata, component_type);
6628 
6629  } else {
6630  if (cdata->_x_size != x_size ||
6631  cdata->_y_size != y_size ||
6632  cdata->_num_components != num_components ||
6633  cdata->_component_type != component_type) {
6634  gobj_cat.error()
6635  << "Texture properties have changed for texture " << get_name()
6636  << " page " << z << ".\n";
6637  return false;
6638  }
6639  }
6640 
6641  return true;
6642 }
6643 
6644 /**
6645  *
6646  */
6647 bool Texture::
6648 do_rescale_texture(CData *cdata) {
6649  int new_x_size = cdata->_x_size;
6650  int new_y_size = cdata->_y_size;
6651  if (cdata->_z_size * cdata->_num_views != 1) {
6652  nassert_raise("rescale_texture() doesn't support 3-d or multiview textures.");
6653  return false;
6654  }
6655 
6656  if (do_adjust_this_size(cdata, new_x_size, new_y_size, get_name(), false)) {
6657  // OK, we have to scale the image.
6658  PNMImage orig_image;
6659  if (!do_store_one(cdata, orig_image, 0, 0)) {
6660  gobj_cat.warning()
6661  << "Couldn't get image in rescale_texture()\n";
6662  return false;
6663  }
6664 
6665  gobj_cat.info()
6666  << "Resizing " << get_name() << " to " << new_x_size << " x "
6667  << new_y_size << "\n";
6668  PNMImage new_image(new_x_size, new_y_size, orig_image.get_num_channels(),
6669  orig_image.get_maxval(), orig_image.get_type(),
6670  orig_image.get_color_space());
6671  new_image.quick_filter_from(orig_image);
6672 
6673  do_clear_ram_image(cdata);
6674  cdata->inc_image_modified();
6675  cdata->_x_size = new_x_size;
6676  cdata->_y_size = new_y_size;
6677  if (!do_load_one(cdata, new_image, get_name(), 0, 0, LoaderOptions())) {
6678  return false;
6679  }
6680 
6681  return true;
6682  }
6683 
6684  // Maybe we should pad the image.
6685  int pad_x_size = 0;
6686  int pad_y_size = 0;
6687  if (do_get_auto_texture_scale(cdata) == ATS_pad) {
6688  new_x_size = cdata->_x_size;
6689  new_y_size = cdata->_y_size;
6690  if (do_adjust_this_size(cdata, new_x_size, new_y_size, get_name(), true)) {
6691  pad_x_size = new_x_size - cdata->_x_size;
6692  pad_y_size = new_y_size - cdata->_y_size;
6693 
6694  PNMImage orig_image;
6695  if (!do_store_one(cdata, orig_image, 0, 0)) {
6696  gobj_cat.warning()
6697  << "Couldn't get image in rescale_texture()\n";
6698  return false;
6699  }
6700  PNMImage new_image(new_x_size, new_y_size, orig_image.get_num_channels(),
6701  orig_image.get_maxval(), orig_image.get_type(),
6702  orig_image.get_color_space());
6703  new_image.copy_sub_image(orig_image, 0, new_y_size - orig_image.get_y_size());
6704 
6705  do_clear_ram_image(cdata);
6706  cdata->_loaded_from_image = false;
6707  cdata->inc_image_modified();
6708  if (!do_load_one(cdata, new_image, get_name(), 0, 0, LoaderOptions())) {
6709  return false;
6710  }
6711 
6712  do_set_pad_size(cdata, pad_x_size, pad_y_size, 0);
6713  return true;
6714  }
6715  }
6716 
6717  // No changes needed.
6718  return false;
6719 }
6720 
6721 /**
6722  *
6723  */
6724 PT(Texture) Texture::
6725 make_copy_impl() const {
6726  CDReader cdata(_cycler);
6727  return do_make_copy(cdata);
6728 }
6729 
6730 /**
6731  *
6732  */
6733 PT(Texture) Texture::
6734 do_make_copy(const CData *cdata) const {
6735  PT(Texture) tex = new Texture(get_name());
6736  CDWriter cdata_tex(tex->_cycler, true);
6737  tex->do_assign(cdata_tex, this, cdata);
6738  return tex;
6739 }
6740 
6741 /**
6742  * The internal implementation of operator =(). Assumes the lock is already
6743  * held on both Textures.
6744  */
6745 void Texture::
6746 do_assign(CData *cdata, const Texture *copy, const CData *cdata_copy) {
6747  cdata->do_assign(cdata_copy);
6748 }
6749 
6750 /**
6751  * The protected implementation of clear(). Assumes the lock is already held.
6752  */
6753 void Texture::
6754 do_clear(CData *cdata) {
6755  Texture tex;
6756  tex.local_object();
6757  CDReader cdata_tex(tex._cycler);
6758  do_assign(cdata, &tex, cdata_tex);
6759 
6760  cdata->inc_properties_modified();
6761  cdata->inc_image_modified();
6762  cdata->inc_simple_image_modified();
6763 }
6764 
6765 /**
6766  *
6767  */
6768 void Texture::
6769 do_setup_texture(CData *cdata, Texture::TextureType texture_type,
6770  int x_size, int y_size, int z_size,
6771  Texture::ComponentType component_type,
6772  Texture::Format format) {
6773  switch (texture_type) {
6774  case TT_1d_texture:
6775  nassertv(y_size == 1 && z_size == 1);
6776  break;
6777 
6778  case TT_2d_texture:
6779  nassertv(z_size == 1);
6780  break;
6781 
6782  case TT_3d_texture:
6783  break;
6784 
6785  case TT_2d_texture_array:
6786  break;
6787 
6788  case TT_cube_map:
6789  // Cube maps must always consist of six square images.
6790  nassertv(x_size == y_size && z_size == 6);
6791 
6792  // In principle the wrap mode shouldn't mean anything to a cube map, but
6793  // some drivers seem to misbehave if it's other than
6794  // SamplerState::WM_clamp.
6795  cdata->_default_sampler.set_wrap_u(SamplerState::WM_clamp);
6796  cdata->_default_sampler.set_wrap_v(SamplerState::WM_clamp);
6797  cdata->_default_sampler.set_wrap_w(SamplerState::WM_clamp);
6798  break;
6799 
6800  case TT_cube_map_array:
6801  // Cube maps array z_size needs to be a multiple of 6.
6802  nassertv(x_size == y_size && z_size % 6 == 0);
6803 
6804  cdata->_default_sampler.set_wrap_u(SamplerState::WM_clamp);
6805  cdata->_default_sampler.set_wrap_v(SamplerState::WM_clamp);
6806  cdata->_default_sampler.set_wrap_w(SamplerState::WM_clamp);
6807  break;
6808 
6809  case TT_buffer_texture:
6810  nassertv(y_size == 1 && z_size == 1);
6811  break;
6812 
6813  case TT_1d_texture_array:
6814  nassertv(z_size == 1);
6815  break;
6816  }
6817 
6818  if (texture_type != TT_2d_texture) {
6819  do_clear_simple_ram_image(cdata);
6820  }
6821 
6822  cdata->_texture_type = texture_type;
6823  cdata->_x_size = x_size;
6824  cdata->_y_size = y_size;
6825  cdata->_z_size = z_size;
6826  cdata->_num_views = 1;
6827  do_set_component_type(cdata, component_type);
6828  do_set_format(cdata, format);
6829 
6830  do_clear_ram_image(cdata);
6831  do_set_pad_size(cdata, 0, 0, 0);
6832  cdata->_orig_file_x_size = 0;
6833  cdata->_orig_file_y_size = 0;
6834  cdata->_loaded_from_image = false;
6835  cdata->_loaded_from_txo = false;
6836  cdata->_has_read_pages = false;
6837  cdata->_has_read_mipmaps = false;
6838 }
6839 
6840 /**
6841  *
6842  */
6843 void Texture::
6844 do_set_format(CData *cdata, Texture::Format format) {
6845  if (format == cdata->_format) {
6846  return;
6847  }
6848  cdata->_format = format;
6849  cdata->inc_properties_modified();
6850 
6851  switch (cdata->_format) {
6852  case F_color_index:
6853  case F_depth_stencil:
6854  case F_depth_component:
6855  case F_depth_component16:
6856  case F_depth_component24:
6857  case F_depth_component32:
6858  case F_red:
6859  case F_green:
6860  case F_blue:
6861  case F_alpha:
6862  case F_luminance:
6863  case F_r16:
6864  case F_r16i:
6865  case F_sluminance:
6866  case F_r32i:
6867  case F_r32:
6868  case F_r8i:
6869  cdata->_num_components = 1;
6870  break;
6871 
6872  case F_luminance_alpha:
6873  case F_luminance_alphamask:
6874  case F_rg16:
6875  case F_sluminance_alpha:
6876  case F_rg32:
6877  case F_rg8i:
6878  case F_rg:
6879  cdata->_num_components = 2;
6880  break;
6881 
6882  case F_rgb:
6883  case F_rgb5:
6884  case F_rgb8:
6885  case F_rgb12:
6886  case F_rgb332:
6887  case F_rgb16:
6888  case F_srgb:
6889  case F_rgb32:
6890  case F_rgb8i:
6891  case F_r11_g11_b10:
6892  case F_rgb9_e5:
6893  cdata->_num_components = 3;
6894  break;
6895 
6896  case F_rgba:
6897  case F_rgbm:
6898  case F_rgba4:
6899  case F_rgba5:
6900  case F_rgba8:
6901  case F_rgba12:
6902  case F_rgba16:
6903  case F_rgba32:
6904  case F_srgb_alpha:
6905  case F_rgba8i:
6906  case F_rgb10_a2:
6907  cdata->_num_components = 4;
6908  break;
6909  }
6910 }
6911 
6912 /**
6913  *
6914  */
6915 void Texture::
6916 do_set_component_type(CData *cdata, Texture::ComponentType component_type) {
6917  cdata->_component_type = component_type;
6918 
6919  switch (component_type) {
6920  case T_unsigned_byte:
6921  case T_byte:
6922  cdata->_component_width = 1;
6923  break;
6924 
6925  case T_unsigned_short:
6926  case T_short:
6927  case T_half_float:
6928  cdata->_component_width = 2;
6929  break;
6930 
6931  case T_float:
6932  case T_unsigned_int_24_8:
6933  case T_int:
6934  case T_unsigned_int:
6935  cdata->_component_width = 4;
6936  break;
6937  }
6938 }
6939 
6940 /**
6941  *
6942  */
6943 void Texture::
6944 do_set_x_size(CData *cdata, int x_size) {
6945  if (cdata->_x_size != x_size) {
6946  cdata->_x_size = x_size;
6947  cdata->inc_image_modified();
6948  do_clear_ram_image(cdata);
6949  do_set_pad_size(cdata, 0, 0, 0);
6950  }
6951 }
6952 
6953 /**
6954  *
6955  */
6956 void Texture::
6957 do_set_y_size(CData *cdata, int y_size) {
6958  if (cdata->_y_size != y_size) {
6959  nassertv((cdata->_texture_type != Texture::TT_buffer_texture &&
6960  cdata->_texture_type != Texture::TT_1d_texture) || y_size == 1);
6961  cdata->_y_size = y_size;
6962  cdata->inc_image_modified();
6963  do_clear_ram_image(cdata);
6964  do_set_pad_size(cdata, 0, 0, 0);
6965  }
6966 }
6967 
6968 /**
6969  * Changes the z size indicated for the texture. This also implicitly unloads
6970  * the texture if it has already been loaded.
6971  */
6972 void Texture::
6973 do_set_z_size(CData *cdata, int z_size) {
6974  if (cdata->_z_size != z_size) {
6975  nassertv((cdata->_texture_type == Texture::TT_3d_texture) ||
6976  (cdata->_texture_type == Texture::TT_cube_map && z_size == 6) ||
6977  (cdata->_texture_type == Texture::TT_cube_map_array && z_size % 6 == 0) ||
6978  (cdata->_texture_type == Texture::TT_2d_texture_array) || (z_size == 1));
6979  cdata->_z_size = z_size;
6980  cdata->inc_image_modified();
6981  do_clear_ram_image(cdata);
6982  do_set_pad_size(cdata, 0, 0, 0);
6983  }
6984 }
6985 
6986 /**
6987  *
6988  */
6989 void Texture::
6990 do_set_num_views(CData *cdata, int num_views) {
6991  nassertv(num_views >= 1);
6992  if (cdata->_num_views != num_views) {
6993  cdata->_num_views = num_views;
6994  if (do_has_ram_image(cdata)) {
6995  cdata->inc_image_modified();
6996  do_clear_ram_image(cdata);
6997  }
6998  do_set_pad_size(cdata, 0, 0, 0);
6999  }
7000 }
7001 
7002 /**
7003  *
7004  */
7005 void Texture::
7006 do_set_wrap_u(CData *cdata, SamplerState::WrapMode wrap) {
7007  if (cdata->_default_sampler.get_wrap_u() != wrap) {
7008  cdata->inc_properties_modified();
7009  cdata->_default_sampler.set_wrap_u(wrap);
7010  }
7011 }
7012 
7013 /**
7014  *
7015  */
7016 void Texture::
7017 do_set_wrap_v(CData *cdata, SamplerState::WrapMode wrap) {
7018  if (cdata->_default_sampler.get_wrap_v() != wrap) {
7019  cdata->inc_properties_modified();
7020  cdata->_default_sampler.set_wrap_v(wrap);
7021  }
7022 }
7023 
7024 /**
7025  *
7026  */
7027 void Texture::
7028 do_set_wrap_w(CData *cdata, SamplerState::WrapMode wrap) {
7029  if (cdata->_default_sampler.get_wrap_w() != wrap) {
7030  cdata->inc_properties_modified();
7031  cdata->_default_sampler.set_wrap_w(wrap);
7032  }
7033 }
7034 
7035 /**
7036  *
7037  */
7038 void Texture::
7039 do_set_minfilter(CData *cdata, SamplerState::FilterType filter) {
7040  if (cdata->_default_sampler.get_minfilter() != filter) {
7041  cdata->inc_properties_modified();
7042  cdata->_default_sampler.set_minfilter(filter);
7043  }
7044 }
7045 
7046 /**
7047  *
7048  */
7049 void Texture::
7050 do_set_magfilter(CData *cdata, SamplerState::FilterType filter) {
7051  if (cdata->_default_sampler.get_magfilter() != filter) {
7052  cdata->inc_properties_modified();
7053  cdata->_default_sampler.set_magfilter(filter);
7054  }
7055 }
7056 
7057 /**
7058  *
7059  */
7060 void Texture::
7061 do_set_anisotropic_degree(CData *cdata, int anisotropic_degree) {
7062  if (cdata->_default_sampler.get_anisotropic_degree() != anisotropic_degree) {
7063  cdata->inc_properties_modified();
7064  cdata->_default_sampler.set_anisotropic_degree(anisotropic_degree);
7065  }
7066 }
7067 
7068 /**
7069  *
7070  */
7071 void Texture::
7072 do_set_border_color(CData *cdata, const LColor &color) {
7073  if (cdata->_default_sampler.get_border_color() != color) {
7074  cdata->inc_properties_modified();
7075  cdata->_default_sampler.set_border_color(color);
7076  }
7077 }
7078 
7079 /**
7080  *
7081  */
7082 void Texture::
7083 do_set_compression(CData *cdata, Texture::CompressionMode compression) {
7084  if (cdata->_compression != compression) {
7085  cdata->inc_properties_modified();
7086  cdata->_compression = compression;
7087 
7088  if (do_has_ram_image(cdata)) {
7089  bool has_compression = do_has_compression(cdata);
7090  bool has_ram_image_compression = (cdata->_ram_image_compression != CM_off);
7091  if (has_compression != has_ram_image_compression ||
7092  has_compression) {
7093  // Reload if we're turning compression on or off, or if we're changing
7094  // the compression mode to a different kind of compression.
7095  do_reload(cdata);
7096  }
7097  }
7098  }
7099 }
7100 
7101 /**
7102  *
7103  */
7104 void Texture::
7105 do_set_quality_level(CData *cdata, Texture::QualityLevel quality_level) {
7106  if (cdata->_quality_level != quality_level) {
7107  cdata->inc_properties_modified();
7108  cdata->_quality_level = quality_level;
7109  }
7110 }
7111 
7112 /**
7113  *
7114  */
7115 bool Texture::
7116 do_has_compression(const CData *cdata) const {
7117  if (cdata->_compression == CM_default) {
7118  return compressed_textures;
7119  } else {
7120  return (cdata->_compression != CM_off);
7121  }
7122 }
7123 
7124 /**
7125  * The protected implementation of has_ram_image(). Assumes the lock is
7126  * already held.
7127  */
7128 bool Texture::
7129 do_has_ram_image(const CData *cdata) const {
7130  return !cdata->_ram_images.empty() && !cdata->_ram_images[0]._image.empty();
7131 }
7132 
7133 /**
7134  * The protected implementation of has_uncompressed_ram_image(). Assumes the
7135  * lock is already held.
7136  */
7137 bool Texture::
7138 do_has_uncompressed_ram_image(const CData *cdata) const {
7139  return !cdata->_ram_images.empty() && !cdata->_ram_images[0]._image.empty() && cdata->_ram_image_compression == CM_off;
7140 }
7141 
7142 /**
7143  *
7144  */
7145 CPTA_uchar Texture::
7146 do_get_ram_image(CData *cdata) {
7147  if (!do_has_ram_image(cdata) && do_can_reload(cdata)) {
7148  do_reload_ram_image(cdata, true);
7149 
7150  if (do_has_ram_image(cdata)) {
7151  // Normally, we don't update the cdata->_modified semaphores in a
7152  // do_blah method, but we'll make an exception in this case, because
7153  // it's easiest to modify these here, and only when we know it's needed.
7154  cdata->inc_image_modified();
7155  cdata->inc_properties_modified();
7156  }
7157  }
7158 
7159  if (cdata->_ram_images.empty()) {
7160  return CPTA_uchar(get_class_type());
7161  }
7162 
7163  return cdata->_ram_images[0]._image;
7164 }
7165 
7166 /**
7167  *
7168  */
7169 CPTA_uchar Texture::
7170 do_get_uncompressed_ram_image(CData *cdata) {
7171  if (!cdata->_ram_images.empty() && cdata->_ram_image_compression != CM_off) {
7172  // We have an image in-ram, but it's compressed. Try to uncompress it
7173  // first.
7174  if (do_uncompress_ram_image(cdata)) {
7175  if (gobj_cat.is_debug()) {
7176  gobj_cat.debug()
7177  << "Uncompressed " << get_name() << "\n";
7178  }
7179  return cdata->_ram_images[0]._image;
7180  }
7181  }
7182 
7183  // Couldn't uncompress the existing image. Try to reload it.
7184  if ((!do_has_ram_image(cdata) || cdata->_ram_image_compression != CM_off) && do_can_reload(cdata)) {
7185  do_reload_ram_image(cdata, false);
7186  }
7187 
7188  if (!cdata->_ram_images.empty() && cdata->_ram_image_compression != CM_off) {
7189  // Great, now we have an image.
7190  if (do_uncompress_ram_image(cdata)) {
7191  gobj_cat.info()
7192  << "Uncompressed " << get_name() << "\n";
7193  return cdata->_ram_images[0]._image;
7194  }
7195  }
7196 
7197  if (cdata->_ram_images.empty() || cdata->_ram_image_compression != CM_off) {
7198  return CPTA_uchar(get_class_type());
7199  }
7200 
7201  return cdata->_ram_images[0]._image;
7202 }
7203 
7204 /**
7205  * Returns the uncompressed system-RAM image data associated with the texture.
7206  * Rather than just returning a pointer to the data, like
7207  * get_uncompressed_ram_image, this function first processes the data and
7208  * reorders the components using the specified format string, and places these
7209  * into a new char array.
7210  *
7211  * The 'format' argument should specify in which order the components of the
7212  * texture must be. For example, valid format strings are "RGBA", "GA",
7213  * "ABRG" or "AAA". A component can also be written as "0" or "1", which
7214  * means an empty/black or a full/white channel, respectively.
7215  *
7216  * This function is particularly useful to copy an image in-memory to a
7217  * different library (for example, PIL or wxWidgets) that require a different
7218  * component order than Panda's internal format, BGRA. Note, however, that
7219  * this conversion can still be too slow if you want to do it every frame, and
7220  * should thus be avoided for that purpose.
7221  *
7222  * The only requirement for the reordering is that an uncompressed image must
7223  * be available. If the RAM image is compressed, it will attempt to re-load
7224  * the texture from disk, if it doesn't find an uncompressed image there, it
7225  * will return NULL.
7226  */
7228 get_ram_image_as(const string &requested_format) {
7229  CDWriter cdata(_cycler, false);
7230  string format = upcase(requested_format);
7231 
7232  // Make sure we can grab something that's uncompressed.
7233  CPTA_uchar data = do_get_uncompressed_ram_image(cdata);
7234  if (data == nullptr) {
7235  gobj_cat.error() << "Couldn't find an uncompressed RAM image!\n";
7236  return CPTA_uchar(get_class_type());
7237  }
7238  int imgsize = cdata->_x_size * cdata->_y_size;
7239  nassertr(cdata->_num_components > 0 && cdata->_num_components <= 4, CPTA_uchar(get_class_type()));
7240  nassertr(data.size() == (size_t)(cdata->_component_width * cdata->_num_components * imgsize), CPTA_uchar(get_class_type()));
7241 
7242  // Check if the format is already what we have internally.
7243  if ((cdata->_num_components == 1 && format.size() == 1) ||
7244  (cdata->_num_components == 2 && format.size() == 2 && format.at(1) == 'A' && format.at(0) != 'A') ||
7245  (cdata->_num_components == 3 && format == "BGR") ||
7246  (cdata->_num_components == 4 && format == "BGRA")) {
7247  // The format string is already our format, so we just need to copy it.
7248  return CPTA_uchar(data);
7249  }
7250 
7251  // Check if we have an alpha channel, and remember which channel we use.
7252  int alpha = -1;
7253  if (Texture::has_alpha(cdata->_format)) {
7254  alpha = cdata->_num_components - 1;
7255  }
7256 
7257  // Validate the format beforehand.
7258  for (size_t i = 0; i < format.size(); ++i) {
7259  if (format[i] != 'B' && format[i] != 'G' && format[i] != 'R' &&
7260  format[i] != 'A' && format[i] != '0' && format[i] != '1') {
7261  gobj_cat.error() << "Unexpected component character '"
7262  << format[i] << "', expected one of RGBA01!\n";
7263  return CPTA_uchar(get_class_type());
7264  }
7265  }
7266 
7267  // Create a new empty array that can hold our image.
7268  PTA_uchar newdata = PTA_uchar::empty_array(imgsize * format.size() * cdata->_component_width, get_class_type());
7269 
7270  // These ifs are for optimization of commonly used image types.
7271  if (cdata->_component_width == 1) {
7272  if (format == "RGBA" && cdata->_num_components == 4) {
7273  const uint32_t *src = (const uint32_t *)data.p();
7274  uint32_t *dst = (uint32_t *)newdata.p();
7275 
7276  for (int p = 0; p < imgsize; ++p) {
7277  uint32_t v = *src++;
7278  *dst++ = ((v & 0xff00ff00u)) |
7279  ((v & 0x00ff0000u) >> 16) |
7280  ((v & 0x000000ffu) << 16);
7281  }
7282  return newdata;
7283  }
7284  if (format == "RGB" && cdata->_num_components == 4) {
7285  const uint32_t *src = (const uint32_t *)data.p();
7286  uint32_t *dst = (uint32_t *)newdata.p();
7287 
7288  // Convert blocks of 4 pixels at a time, so that we can treat both the
7289  // source and destination as 32-bit integers.
7290  int blocks = imgsize >> 2;
7291  for (int i = 0; i < blocks; ++i) {
7292  uint32_t v0 = *src++;
7293  uint32_t v1 = *src++;
7294  uint32_t v2 = *src++;
7295  uint32_t v3 = *src++;
7296  *dst++ = ((v0 & 0x00ff0000u) >> 16) |
7297  ((v0 & 0x0000ff00u)) |
7298  ((v0 & 0x000000ffu) << 16) |
7299  ((v1 & 0x00ff0000u) << 8);
7300  *dst++ = ((v1 & 0x0000ff00u) >> 8) |
7301  ((v1 & 0x000000ffu) << 8) |
7302  ((v2 & 0x00ff0000u)) |
7303  ((v2 & 0x0000ff00u) << 16);
7304  *dst++ = ((v2 & 0x000000ffu)) |
7305  ((v3 & 0x00ff0000u) >> 8) |
7306  ((v3 & 0x0000ff00u) << 8) |
7307  ((v3 & 0x000000ffu) << 24);
7308  }
7309 
7310  // If the image size wasn't a multiple of 4, we may have a handful of
7311  // pixels left over. Convert those the slower way.
7312  uint8_t *tail = (uint8_t *)dst;
7313  for (int i = (imgsize & ~0x3); i < imgsize; ++i) {
7314  uint32_t v = *src++;
7315  *tail++ = (v & 0x00ff0000u) >> 16;
7316  *tail++ = (v & 0x0000ff00u) >> 8;
7317  *tail++ = (v & 0x000000ffu);
7318  }
7319  return newdata;
7320  }
7321  if (format == "BGR" && cdata->_num_components == 4) {
7322  const uint32_t *src = (const uint32_t *)data.p();
7323  uint32_t *dst = (uint32_t *)newdata.p();
7324 
7325  // Convert blocks of 4 pixels at a time, so that we can treat both the
7326  // source and destination as 32-bit integers.
7327  int blocks = imgsize >> 2;
7328  for (int i = 0; i < blocks; ++i) {
7329  uint32_t v0 = *src++;
7330  uint32_t v1 = *src++;
7331  uint32_t v2 = *src++;
7332  uint32_t v3 = *src++;
7333  *dst++ = (v0 & 0x00ffffffu) | ((v1 & 0x000000ffu) << 24);
7334  *dst++ = ((v1 & 0x00ffff00u) >> 8) | ((v2 & 0x0000ffffu) << 16);
7335  *dst++ = ((v2 & 0x00ff0000u) >> 16) | ((v3 & 0x00ffffffu) << 8);
7336  }
7337 
7338  // If the image size wasn't a multiple of 4, we may have a handful of
7339  // pixels left over. Convert those the slower way.
7340  uint8_t *tail = (uint8_t *)dst;
7341  for (int i = (imgsize & ~0x3); i < imgsize; ++i) {
7342  uint32_t v = *src++;
7343  *tail++ = (v & 0x000000ffu);
7344  *tail++ = (v & 0x0000ff00u) >> 8;
7345  *tail++ = (v & 0x00ff0000u) >> 16;
7346  }
7347  return newdata;
7348  }
7349  const uint8_t *src = (const uint8_t *)data.p();
7350  uint8_t *dst = (uint8_t *)newdata.p();
7351 
7352  if (format == "RGB" && cdata->_num_components == 3) {
7353  for (int i = 0; i < imgsize; ++i) {
7354  *dst++ = src[2];
7355  *dst++ = src[1];
7356  *dst++ = src[0];
7357  src += 3;
7358  }
7359  return newdata;
7360  }
7361  if (format == "A" && cdata->_num_components != 3) {
7362  // We can generally rely on alpha to be the last component.
7363  for (int p = 0; p < imgsize; ++p) {
7364  dst[p] = src[alpha];
7365  src += cdata->_num_components;
7366  }
7367  return newdata;
7368  }
7369  // Fallback case for other 8-bit-per-channel formats.
7370  for (int p = 0; p < imgsize; ++p) {
7371  for (size_t i = 0; i < format.size(); ++i) {
7372  if (format[i] == 'B' || (cdata->_num_components <= 2 && format[i] != 'A')) {
7373  *dst++ = src[0];
7374  } else if (format[i] == 'G') {
7375  *dst++ = src[1];
7376  } else if (format[i] == 'R') {
7377  *dst++ = src[2];
7378  } else if (format[i] == 'A') {
7379  if (alpha >= 0) {
7380  *dst++ = src[alpha];
7381  } else {
7382  *dst++ = 0xff;
7383  }
7384  } else if (format[i] == '1') {
7385  *dst++ = 0xff;
7386  } else {
7387  *dst++ = 0x00;
7388  }
7389  }
7390  src += cdata->_num_components;
7391  }
7392  return newdata;
7393  }
7394 
7395  // The slow and general case.
7396  for (int p = 0; p < imgsize; ++p) {
7397  for (size_t i = 0; i < format.size(); ++i) {
7398  int component = 0;
7399  if (format[i] == 'B' || (cdata->_num_components <= 2 && format[i] != 'A')) {
7400  component = 0;
7401  } else if (format[i] == 'G') {
7402  component = 1;
7403  } else if (format[i] == 'R') {
7404  component = 2;
7405  } else if (format[i] == 'A') {
7406  if (alpha >= 0) {
7407  component = alpha;
7408  } else {
7409  memset((void*)(newdata + (p * format.size() + i) * cdata->_component_width), -1, cdata->_component_width);
7410  continue;
7411  }
7412  } else if (format[i] == '1') {
7413  memset((void*)(newdata + (p * format.size() + i) * cdata->_component_width), -1, cdata->_component_width);
7414  continue;
7415  } else {
7416  memset((void*)(newdata + (p * format.size() + i) * cdata->_component_width), 0, cdata->_component_width);
7417  continue;
7418  }
7419  memcpy((void*)(newdata + (p * format.size() + i) * cdata->_component_width),
7420  (void*)(data + (p * cdata->_num_components + component) * cdata->_component_width),
7421  cdata->_component_width);
7422  }
7423  }
7424  return newdata;
7425 }
7426 
7427 /**
7428  *
7429  */
7430 void Texture::
7431 do_set_simple_ram_image(CData *cdata, CPTA_uchar image, int x_size, int y_size) {
7432  nassertv(cdata->_texture_type == TT_2d_texture);
7433  size_t expected_page_size = (size_t)(x_size * y_size * 4);
7434  nassertv(image.size() == expected_page_size);
7435 
7436  cdata->_simple_x_size = x_size;
7437  cdata->_simple_y_size = y_size;
7438  cdata->_simple_ram_image._image = image.cast_non_const();
7439  cdata->_simple_ram_image._page_size = image.size();
7440  cdata->_simple_image_date_generated = (int32_t)time(nullptr);
7441  cdata->inc_simple_image_modified();
7442 }
7443 
7444 /**
7445  *
7446  */
7447 int Texture::
7448 do_get_expected_num_mipmap_levels(const CData *cdata) const {
7449  if (cdata->_texture_type == Texture::TT_buffer_texture) {
7450  return 1;
7451  }
7452  int size = max(cdata->_x_size, cdata->_y_size);
7453  if (cdata->_texture_type == Texture::TT_3d_texture) {
7454  size = max(size, cdata->_z_size);
7455  }
7456  int count = 1;
7457  while (size > 1) {
7458  size >>= 1;
7459  ++count;
7460  }
7461  return count;
7462 }
7463 
7464 /**
7465  *
7466  */
7467 size_t Texture::
7468 do_get_ram_mipmap_page_size(const CData *cdata, int n) const {
7469  if (cdata->_ram_image_compression != CM_off) {
7470  if (n >= 0 && n < (int)cdata->_ram_images.size()) {
7471  return cdata->_ram_images[n]._page_size;
7472  }
7473  return 0;
7474  } else {
7475  return do_get_expected_ram_mipmap_page_size(cdata, n);
7476  }
7477 }
7478 
7479 /**
7480  *
7481  */
7482 int Texture::
7483 do_get_expected_mipmap_x_size(const CData *cdata, int n) const {
7484  int size = max(cdata->_x_size, 1);
7485  while (n > 0 && size > 1) {
7486  size >>= 1;
7487  --n;
7488  }
7489  return size;
7490 }
7491 
7492 /**
7493  *
7494  */
7495 int Texture::
7496 do_get_expected_mipmap_y_size(const CData *cdata, int n) const {
7497  int size = max(cdata->_y_size, 1);
7498  while (n > 0 && size > 1) {
7499  size >>= 1;
7500  --n;
7501  }
7502  return size;
7503 }
7504 
7505 /**
7506  *
7507  */
7508 int Texture::
7509 do_get_expected_mipmap_z_size(const CData *cdata, int n) const {
7510  // 3-D textures have a different number of pages per each mipmap level.
7511  // Other kinds of textures--especially, cube map textures--always have the
7512  // same.
7513  if (cdata->_texture_type == Texture::TT_3d_texture) {
7514  int size = max(cdata->_z_size, 1);
7515  while (n > 0 && size > 1) {
7516  size >>= 1;
7517  --n;
7518  }
7519  return size;
7520 
7521  } else {
7522  return cdata->_z_size;
7523  }
7524 }
7525 
7526 /**
7527  *
7528  */
7529 void Texture::
7530 do_clear_simple_ram_image(CData *cdata) {
7531  cdata->_simple_x_size = 0;
7532  cdata->_simple_y_size = 0;
7533  cdata->_simple_ram_image._image.clear();
7534  cdata->_simple_ram_image._page_size = 0;
7535  cdata->_simple_image_date_generated = 0;
7536 
7537  // We allow this exception: we update the _simple_image_modified here, since
7538  // no one really cares much about that anyway, and it's convenient to do it
7539  // here.
7540  cdata->inc_simple_image_modified();
7541 }
7542 
7543 /**
7544  *
7545  */
7546 void Texture::
7547 do_clear_ram_mipmap_images(CData *cdata) {
7548  if (!cdata->_ram_images.empty()) {
7549  cdata->_ram_images.erase(cdata->_ram_images.begin() + 1, cdata->_ram_images.end());
7550  }
7551 }
7552 
7553 /**
7554  * Generates the RAM mipmap images for this texture, first uncompressing it as
7555  * required. Will recompress the image if it was originally compressed,
7556  * unless allow_recompress is true.
7557  */
7558 void Texture::
7559 do_generate_ram_mipmap_images(CData *cdata, bool allow_recompress) {
7560  nassertv(do_has_ram_image(cdata));
7561 
7562  if (do_get_expected_num_mipmap_levels(cdata) == 1) {
7563  // Don't bother.
7564  return;
7565  }
7566 
7567  RamImage orig_compressed_image;
7568  CompressionMode orig_compression_mode = CM_off;
7569 
7570  if (cdata->_ram_image_compression != CM_off) {
7571  // The RAM image is compressed. This means we need to uncompress it in
7572  // order to generate mipmap images. Save the original first, to avoid
7573  // lossy recompression.
7574  orig_compressed_image = cdata->_ram_images[0];
7575  orig_compression_mode = cdata->_ram_image_compression;
7576 
7577  // Now try to get the uncompressed source image.
7578  do_get_uncompressed_ram_image(cdata);
7579 
7580  if (cdata->_ram_image_compression != CM_off) {
7581  gobj_cat.error()
7582  << "Cannot generate mipmap levels for image with compression "
7583  << cdata->_ram_image_compression << "\n";
7584  return;
7585  }
7586  }
7587 
7588  do_clear_ram_mipmap_images(cdata);
7589 
7590  if (gobj_cat.is_debug()) {
7591  gobj_cat.debug()
7592  << "Generating mipmap levels for " << *this << "\n";
7593  }
7594 
7595  if (cdata->_texture_type == Texture::TT_3d_texture && cdata->_z_size != 1) {
7596  // Eek, a 3-D texture.
7597  int x_size = cdata->_x_size;
7598  int y_size = cdata->_y_size;
7599  int z_size = cdata->_z_size;
7600  int n = 0;
7601  while (x_size > 1 || y_size > 1 || z_size > 1) {
7602  cdata->_ram_images.push_back(RamImage());
7603  do_filter_3d_mipmap_level(cdata, cdata->_ram_images[n + 1], cdata->_ram_images[n],
7604  x_size, y_size, z_size);
7605  x_size = max(x_size >> 1, 1);
7606  y_size = max(y_size >> 1, 1);
7607  z_size = max(z_size >> 1, 1);
7608  ++n;
7609  }
7610 
7611  } else {
7612  // A 1-D, 2-D, or cube map texture.
7613  int x_size = cdata->_x_size;
7614  int y_size = cdata->_y_size;
7615  int n = 0;
7616  while (x_size > 1 || y_size > 1) {
7617  cdata->_ram_images.push_back(RamImage());
7618  do_filter_2d_mipmap_pages(cdata, cdata->_ram_images[n + 1], cdata->_ram_images[n],
7619  x_size, y_size);
7620  x_size = max(x_size >> 1, 1);
7621  y_size = max(y_size >> 1, 1);
7622  ++n;
7623  }
7624  }
7625 
7626  if (orig_compression_mode != CM_off && allow_recompress) {
7627  // Now attempt to recompress the mipmap images according to the original
7628  // compression mode. We don't need to bother compressing the first image
7629  // (it was already compressed, after all), so temporarily remove it from
7630  // the top of the mipmap stack, and compress all of the rest of them
7631  // instead.
7632  nassertv(cdata->_ram_images.size() > 1);
7633  int l0_x_size = cdata->_x_size;
7634  int l0_y_size = cdata->_y_size;
7635  int l0_z_size = cdata->_z_size;
7636  cdata->_x_size = do_get_expected_mipmap_x_size(cdata, 1);
7637  cdata->_y_size = do_get_expected_mipmap_y_size(cdata, 1);
7638  cdata->_z_size = do_get_expected_mipmap_z_size(cdata, 1);
7639  RamImage uncompressed_image = cdata->_ram_images[0];
7640  cdata->_ram_images.erase(cdata->_ram_images.begin());
7641 
7642  bool success = do_compress_ram_image(cdata, orig_compression_mode, QL_default, nullptr);
7643  // Now restore the toplevel image.
7644  if (success) {
7645  if (gobj_cat.is_debug()) {
7646  gobj_cat.debug()
7647  << "Compressed " << get_name() << " generated mipmaps with "
7648  << cdata->_ram_image_compression << "\n";
7649  }
7650  cdata->_ram_images.insert(cdata->_ram_images.begin(), orig_compressed_image);
7651  } else {
7652  cdata->_ram_images.insert(cdata->_ram_images.begin(), uncompressed_image);
7653  }
7654  cdata->_x_size = l0_x_size;
7655  cdata->_y_size = l0_y_size;
7656  cdata->_z_size = l0_z_size;
7657  }
7658 }
7659 
7660 /**
7661  *
7662  */
7663 void Texture::
7664 do_set_pad_size(CData *cdata, int x, int y, int z) {
7665  if (x > cdata->_x_size) {
7666  x = cdata->_x_size;
7667  }
7668  if (y > cdata->_y_size) {
7669  y = cdata->_y_size;
7670  }
7671  if (z > cdata->_z_size) {
7672  z = cdata->_z_size;
7673  }
7674 
7675  cdata->_pad_x_size = x;
7676  cdata->_pad_y_size = y;
7677  cdata->_pad_z_size = z;
7678 }
7679 
7680 /**
7681  * Returns true if we can safely call do_reload_ram_image() in order to make
7682  * the image available, or false if we shouldn't do this (because we know from
7683  * a priori knowledge that it wouldn't work anyway).
7684  */
7685 bool Texture::
7686 do_can_reload(const CData *cdata) const {
7687  return (cdata->_loaded_from_image && !cdata->_fullpath.empty());
7688 }
7689 
7690 /**
7691  *
7692  */
7693 bool Texture::
7694 do_reload(CData *cdata) {
7695  if (do_can_reload(cdata)) {
7696  do_clear_ram_image(cdata);
7697  do_reload_ram_image(cdata, true);
7698  if (do_has_ram_image(cdata)) {
7699  // An explicit call to reload() should increment image_modified.
7700  cdata->inc_image_modified();
7701  return true;
7702  }
7703  return false;
7704  }
7705 
7706  // We don't have a filename to load from.
7707  return false;
7708 }
7709 
7710 /**
7711  * Returns true if there is a rawdata image that we have available to write to
7712  * the bam stream. For a normal Texture, this is the same thing as
7713  * do_has_ram_image(), but a movie texture might define it differently.
7714  */
7715 bool Texture::
7716 do_has_bam_rawdata(const CData *cdata) const {
7717  return do_has_ram_image(cdata);
7718 }
7719 
7720 /**
7721  * If do_has_bam_rawdata() returned false, this attempts to reload the rawdata
7722  * image if possible.
7723  */
7724 void Texture::
7725 do_get_bam_rawdata(CData *cdata) {
7726  do_get_ram_image(cdata);
7727 }
7728 
7729 /**
7730  * Internal method to convert pixel data from the indicated PNMImage into the
7731  * given ram_image.
7732  */
7733 void Texture::
7734 convert_from_pnmimage(PTA_uchar &image, size_t page_size,
7735  int row_stride, int x, int y, int z,
7736  const PNMImage &pnmimage, int num_components,
7737  int component_width) {
7738  int x_size = pnmimage.get_x_size();
7739  int y_size = pnmimage.get_y_size();
7740  xelval maxval = pnmimage.get_maxval();
7741  int pixel_size = num_components * component_width;
7742 
7743  int row_skip = 0;
7744  if (row_stride == 0) {
7745  row_stride = x_size;
7746  } else {
7747  row_skip = (row_stride - x_size) * pixel_size;
7748  nassertv(row_skip >= 0);
7749  }
7750 
7751  bool is_grayscale = (num_components == 1 || num_components == 2);
7752  bool has_alpha = (num_components == 2 || num_components == 4);
7753  bool img_has_alpha = pnmimage.has_alpha();
7754 
7755  int idx = page_size * z;
7756  nassertv(idx + page_size <= image.size());
7757  unsigned char *p = &image[idx];
7758 
7759  if (x != 0 || y != 0) {
7760  p += (row_stride * y + x) * pixel_size;
7761  }
7762 
7763  if (maxval == 255 && component_width == 1) {
7764  // Most common case: one byte per pixel, and the source image shows a
7765  // maxval of 255. No scaling is necessary. Because this is such a common
7766  // case, we break it out per component for best performance.
7767  const xel *array = pnmimage.get_array();
7768  switch (num_components) {
7769  case 1:
7770  for (int j = y_size-1; j >= 0; j--) {
7771  const xel *row = array + j * x_size;
7772  for (int i = 0; i < x_size; i++) {
7773  *p++ = (uchar)PPM_GETB(row[i]);
7774  }
7775  p += row_skip;
7776  }
7777  break;
7778 
7779  case 2:
7780  if (img_has_alpha) {
7781  const xelval *alpha = pnmimage.get_alpha_array();
7782  for (int j = y_size-1; j >= 0; j--) {
7783  const xel *row = array + j * x_size;
7784  const xelval *alpha_row = alpha + j * x_size;
7785  for (int i = 0; i < x_size; i++) {
7786  *p++ = (uchar)PPM_GETB(row[i]);
7787  *p++ = (uchar)alpha_row[i];
7788  }
7789  p += row_skip;
7790  }
7791  } else {
7792  for (int j = y_size-1; j >= 0; j--) {
7793  const xel *row = array + j * x_size;
7794  for (int i = 0; i < x_size; i++) {
7795  *p++ = (uchar)PPM_GETB(row[i]);
7796  *p++ = (uchar)255;
7797  }
7798  p += row_skip;
7799  }
7800  }
7801  break;
7802 
7803  case 3:
7804  for (int j = y_size-1; j >= 0; j--) {
7805  const xel *row = array + j * x_size;
7806  for (int i = 0; i < x_size; i++) {
7807  *p++ = (uchar)PPM_GETB(row[i]);
7808  *p++ = (uchar)PPM_GETG(row[i]);
7809  *p++ = (uchar)PPM_GETR(row[i]);
7810  }
7811  p += row_skip;
7812  }
7813  break;
7814 
7815  case 4:
7816  if (img_has_alpha) {
7817  const xelval *alpha = pnmimage.get_alpha_array();
7818  for (int j = y_size-1; j >= 0; j--) {
7819  const xel *row = array + j * x_size;
7820  const xelval *alpha_row = alpha + j * x_size;
7821  for (int i = 0; i < x_size; i++) {
7822  *p++ = (uchar)PPM_GETB(row[i]);
7823  *p++ = (uchar)PPM_GETG(row[i]);
7824  *p++ = (uchar)PPM_GETR(row[i]);
7825  *p++ = (uchar)alpha_row[i];
7826  }
7827  p += row_skip;
7828  }
7829  } else {
7830  for (int j = y_size-1; j >= 0; j--) {
7831  const xel *row = array + j * x_size;
7832  for (int i = 0; i < x_size; i++) {
7833  *p++ = (uchar)PPM_GETB(row[i]);
7834  *p++ = (uchar)PPM_GETG(row[i]);
7835  *p++ = (uchar)PPM_GETR(row[i]);
7836  *p++ = (uchar)255;
7837  }
7838  p += row_skip;
7839  }
7840  }
7841  break;
7842 
7843  default:
7844  nassertv(num_components >= 1 && num_components <= 4);
7845  break;
7846  }
7847 
7848  } else if (maxval == 65535 && component_width == 2) {
7849  // Another possible case: two bytes per pixel, and the source image shows
7850  // a maxval of 65535. Again, no scaling is necessary.
7851  for (int j = y_size-1; j >= 0; j--) {
7852  for (int i = 0; i < x_size; i++) {
7853  if (is_grayscale) {
7854  store_unscaled_short(p, pnmimage.get_gray_val(i, j));
7855  } else {
7856  store_unscaled_short(p, pnmimage.get_blue_val(i, j));
7857  store_unscaled_short(p, pnmimage.get_green_val(i, j));
7858  store_unscaled_short(p, pnmimage.get_red_val(i, j));
7859  }
7860  if (has_alpha) {
7861  if (img_has_alpha) {
7862  store_unscaled_short(p, pnmimage.get_alpha_val(i, j));
7863  } else {
7864  store_unscaled_short(p, 65535);
7865  }
7866  }
7867  }
7868  p += row_skip;
7869  }
7870 
7871  } else if (component_width == 1) {
7872  // A less common case: one byte per pixel, but the maxval is something
7873  // other than 255. In this case, we should scale the pixel values up to
7874  // the appropriate amount.
7875  double scale = 255.0 / (double)maxval;
7876 
7877  for (int j = y_size-1; j >= 0; j--) {
7878  for (int i = 0; i < x_size; i++) {
7879  if (is_grayscale) {
7880  store_scaled_byte(p, pnmimage.get_gray_val(i, j), scale);
7881  } else {
7882  store_scaled_byte(p, pnmimage.get_blue_val(i, j), scale);
7883  store_scaled_byte(p, pnmimage.get_green_val(i, j), scale);
7884  store_scaled_byte(p, pnmimage.get_red_val(i, j), scale);
7885  }
7886  if (has_alpha) {
7887  if (img_has_alpha) {
7888  store_scaled_byte(p, pnmimage.get_alpha_val(i, j), scale);
7889  } else {
7890  store_unscaled_byte(p, 255);
7891  }
7892  }
7893  }
7894  p += row_skip;
7895  }
7896 
7897  } else { // component_width == 2
7898  // Another uncommon case: two bytes per pixel, and the maxval is something
7899  // other than 65535. Again, we must scale the pixel values.
7900  double scale = 65535.0 / (double)maxval;
7901 
7902  for (int j = y_size-1; j >= 0; j--) {
7903  for (int i = 0; i < x_size; i++) {
7904  if (is_grayscale) {
7905  store_scaled_short(p, pnmimage.get_gray_val(i, j), scale);
7906  } else {
7907  store_scaled_short(p, pnmimage.get_blue_val(i, j), scale);
7908  store_scaled_short(p, pnmimage.get_green_val(i, j), scale);
7909  store_scaled_short(p, pnmimage.get_red_val(i, j), scale);
7910  }
7911  if (has_alpha) {
7912  if (img_has_alpha) {
7913  store_scaled_short(p, pnmimage.get_alpha_val(i, j), 1.0);
7914  } else {
7915  store_unscaled_short(p, 65535);
7916  }
7917  }
7918  }
7919  p += row_skip;
7920  }
7921  }
7922 }
7923 
7924 /**
7925  * Internal method to convert pixel data from the indicated PfmFile into the
7926  * given ram_image.
7927  */
7928 void Texture::
7929 convert_from_pfm(PTA_uchar &image, size_t page_size, int z,
7930  const PfmFile &pfm, int num_components, int component_width) {
7931  nassertv(component_width == 4); // Currently only PN_float32 is expected.
7932  int x_size = pfm.get_x_size();
7933  int y_size = pfm.get_y_size();
7934 
7935  int idx = page_size * z;
7936  nassertv(idx + page_size <= image.size());
7937  PN_float32 *p = (PN_float32 *)&image[idx];
7938 
7939  switch (num_components) {
7940  case 1:
7941  {
7942  for (int j = y_size-1; j >= 0; j--) {
7943  for (int i = 0; i < x_size; i++) {
7944  p[0] = pfm.get_channel(i, j, 0);
7945  ++p;
7946  }
7947  }
7948  }
7949  break;
7950 
7951  case 2:
7952  {
7953  for (int j = y_size-1; j >= 0; j--) {
7954  for (int i = 0; i < x_size; i++) {
7955  p[0] = pfm.get_channel(i, j, 0);
7956  p[1] = pfm.get_channel(i, j, 1);
7957  p += 2;
7958  }
7959  }
7960  }
7961  break;
7962 
7963  case 3:
7964  {
7965  // RGB -> BGR
7966  for (int j = y_size-1; j >= 0; j--) {
7967  for (int i = 0; i < x_size; i++) {
7968  p[0] = pfm.get_channel(i, j, 2);
7969  p[1] = pfm.get_channel(i, j, 1);
7970  p[2] = pfm.get_channel(i, j, 0);
7971  p += 3;
7972  }
7973  }
7974  }
7975  break;
7976 
7977  case 4:
7978  {
7979  // RGBA -> BGRA
7980  for (int j = y_size-1; j >= 0; j--) {
7981  for (int i = 0; i < x_size; i++) {
7982  p[0] = pfm.get_channel(i, j, 2);
7983  p[1] = pfm.get_channel(i, j, 1);
7984  p[2] = pfm.get_channel(i, j, 0);
7985  p[3] = pfm.get_channel(i, j, 3);
7986  p += 4;
7987  }
7988  }
7989  }
7990  break;
7991 
7992  default:
7993  nassert_raise("unexpected channel count");
7994  return;
7995  }
7996 
7997  nassertv((unsigned char *)p == &image[idx] + page_size);
7998 }
7999 
8000 /**
8001  * Internal method to convert pixel data to the indicated PNMImage from the
8002  * given ram_image.
8003  */
8004 bool Texture::
8005 convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
8006  int num_components, ComponentType component_type,
8007  bool is_srgb, CPTA_uchar image, size_t page_size, int z) {
8008  xelval maxval = 0xff;
8009  if (component_type != T_unsigned_byte && component_type != T_byte) {
8010  maxval = 0xffff;
8011  }
8012  ColorSpace color_space = is_srgb ? CS_sRGB : CS_linear;
8013  pnmimage.clear(x_size, y_size, num_components, maxval, nullptr, color_space);
8014  bool has_alpha = pnmimage.has_alpha();
8015  bool is_grayscale = pnmimage.is_grayscale();
8016 
8017  int idx = page_size * z;
8018  nassertr(idx + page_size <= image.size(), false);
8019 
8020  xel *array = pnmimage.get_array();
8021  xelval *alpha = pnmimage.get_alpha_array();
8022 
8023  switch (component_type) {
8024  case T_unsigned_byte:
8025  if (is_grayscale) {
8026  const unsigned char *p = &image[idx];
8027  if (has_alpha) {
8028  for (int j = y_size-1; j >= 0; j--) {
8029  xel *row = array + j * x_size;
8030  xelval *alpha_row = alpha + j * x_size;
8031  for (int i = 0; i < x_size; i++) {
8032  PPM_PUTB(row[i], *p++);
8033  alpha_row[i] = *p++;
8034  }
8035  }
8036  } else {
8037  for (int j = y_size-1; j >= 0; j--) {
8038  xel *row = array + j * x_size;
8039  for (int i = 0; i < x_size; i++) {
8040  PPM_PUTB(row[i], *p++);
8041  }
8042  }
8043  }
8044  nassertr(p == &image[idx] + page_size, false);
8045  } else {
8046  const unsigned char *p = &image[idx];
8047  if (has_alpha) {
8048  for (int j = y_size-1; j >= 0; j--) {
8049  xel *row = array + j * x_size;
8050  xelval *alpha_row = alpha + j * x_size;
8051  for (int i = 0; i < x_size; i++) {
8052  PPM_PUTB(row[i], *p++);
8053  PPM_PUTG(row[i], *p++);
8054  PPM_PUTR(row[i], *p++);
8055  alpha_row[i] = *p++;
8056  }
8057  }
8058  } else {
8059  for (int j = y_size-1; j >= 0; j--) {
8060  xel *row = array + j * x_size;
8061  for (int i = 0; i < x_size; i++) {
8062  PPM_PUTB(row[i], *p++);
8063  PPM_PUTG(row[i], *p++);
8064  PPM_PUTR(row[i], *p++);
8065  }
8066  }
8067  }
8068  nassertr(p == &image[idx] + page_size, false);
8069  }
8070  break;
8071 
8072  case T_unsigned_short:
8073  {
8074  const uint16_t *p = (const uint16_t *)&image[idx];
8075 
8076  for (int j = y_size-1; j >= 0; j--) {
8077  xel *row = array + j * x_size;
8078  xelval *alpha_row = alpha + j * x_size;
8079  for (int i = 0; i < x_size; i++) {
8080  PPM_PUTB(row[i], *p++);
8081  if (!is_grayscale) {
8082  PPM_PUTG(row[i], *p++);
8083  PPM_PUTR(row[i], *p++);
8084  }
8085  if (has_alpha) {
8086  alpha_row[i] = *p++;
8087  }
8088  }
8089  }
8090  nassertr((const unsigned char *)p == &image[idx] + page_size, false);
8091  }
8092  break;
8093 
8094  case T_unsigned_int:
8095  {
8096  const uint32_t *p = (const uint32_t *)&image[idx];
8097 
8098  for (int j = y_size-1; j >= 0; j--) {
8099  xel *row = array + j * x_size;
8100  xelval *alpha_row = alpha + j * x_size;
8101  for (int i = 0; i < x_size; i++) {
8102  PPM_PUTB(row[i], (*p++) >> 16u);
8103  if (!is_grayscale) {
8104  PPM_PUTG(row[i], (*p++) >> 16u);
8105  PPM_PUTR(row[i], (*p++) >> 16u);
8106  }
8107  if (has_alpha) {
8108  alpha_row[i] = (*p++) >> 16u;
8109  }
8110  }
8111  }
8112  nassertr((const unsigned char *)p == &image[idx] + page_size, false);
8113  }
8114  break;
8115 
8116  case T_half_float:
8117  {
8118  const unsigned char *p = &image[idx];
8119 
8120  for (int j = y_size-1; j >= 0; j--) {
8121  for (int i = 0; i < x_size; i++) {
8122  pnmimage.set_blue(i, j, get_half_float(p));
8123  if (!is_grayscale) {
8124  pnmimage.set_green(i, j, get_half_float(p));
8125  pnmimage.set_red(i, j, get_half_float(p));
8126  }
8127  if (has_alpha) {
8128  pnmimage.set_alpha(i, j, get_half_float(p));
8129  }
8130  }
8131  }
8132  nassertr(p == &image[idx] + page_size, false);
8133  }
8134  break;
8135 
8136  default:
8137  return false;
8138  }
8139 
8140  return true;
8141 }
8142 
8143 /**
8144  * Internal method to convert pixel data to the indicated PfmFile from the
8145  * given ram_image.
8146  */
8147 bool Texture::
8148 convert_to_pfm(PfmFile &pfm, int x_size, int y_size,
8149  int num_components, int component_width,
8150  CPTA_uchar image, size_t page_size, int z) {
8151  nassertr(component_width == 4, false); // Currently only PN_float32 is expected.
8152  pfm.clear(x_size, y_size, num_components);
8153 
8154  int idx = page_size * z;
8155  nassertr(idx + page_size <= image.size(), false);
8156  const PN_float32 *p = (const PN_float32 *)&image[idx];
8157 
8158  switch (num_components) {
8159  case 1:
8160  for (int j = y_size-1; j >= 0; j--) {
8161  for (int i = 0; i < x_size; i++) {
8162  pfm.set_channel(i, j, 0, p[0]);
8163  ++p;
8164  }
8165  }
8166  break;
8167 
8168  case 2:
8169  for (int j = y_size-1; j >= 0; j--) {
8170  for (int i = 0; i < x_size; i++) {
8171  pfm.set_channel(i, j, 0, p[0]);
8172  pfm.set_channel(i, j, 1, p[1]);
8173  p += 2;
8174  }
8175  }
8176  break;
8177 
8178  case 3:
8179  // BGR -> RGB
8180  for (int j = y_size-1; j >= 0; j--) {
8181  for (int i = 0; i < x_size; i++) {
8182  pfm.set_channel(i, j, 2, p[0]);
8183  pfm.set_channel(i, j, 1, p[1]);
8184  pfm.set_channel(i, j, 0, p[2]);
8185  p += 3;
8186  }
8187  }
8188  break;
8189 
8190  case 4:
8191  // BGRA -> RGBA
8192  for (int j = y_size-1; j >= 0; j--) {
8193  for (int i = 0; i < x_size; i++) {
8194  pfm.set_channel(i, j, 2, p[0]);
8195  pfm.set_channel(i, j, 1, p[1]);
8196  pfm.set_channel(i, j, 0, p[2]);
8197  pfm.set_channel(i, j, 3, p[3]);
8198  p += 4;
8199  }
8200  }
8201  break;
8202 
8203  default:
8204  nassert_raise("unexpected channel count");
8205  return false;
8206  }
8207 
8208  nassertr((unsigned char *)p == &image[idx] + page_size, false);
8209  return true;
8210 }
8211 
8212 /**
8213  * Called by read_dds for a DDS file in BGR8 format.
8214  */
8215 PTA_uchar Texture::
8216 read_dds_level_bgr8(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
8217  // This is in order B, G, R.
8218  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8219  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8220 
8221  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
8222  size_t row_bytes = x_size * 3;
8223  PTA_uchar image = PTA_uchar::empty_array(size);
8224  for (int y = y_size - 1; y >= 0; --y) {
8225  unsigned char *p = image.p() + y * row_bytes;
8226  nassertr(p + row_bytes <= image.p() + size, PTA_uchar());
8227  in.read((char *)p, row_bytes);
8228  }
8229 
8230  return image;
8231 }
8232 
8233 /**
8234  * Called by read_dds for a DDS file in RGB8 format.
8235  */
8236 PTA_uchar Texture::
8237 read_dds_level_rgb8(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
8238  // This is in order R, G, B.
8239  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8240  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8241 
8242  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
8243  size_t row_bytes = x_size * 3;
8244  PTA_uchar image = PTA_uchar::empty_array(size);
8245  for (int y = y_size - 1; y >= 0; --y) {
8246  unsigned char *p = image.p() + y * row_bytes;
8247  nassertr(p + row_bytes <= image.p() + size, PTA_uchar());
8248  in.read((char *)p, row_bytes);
8249 
8250  // Now reverse the r, g, b triples.
8251  for (int x = 0; x < x_size; ++x) {
8252  unsigned char r = p[0];
8253  p[0] = p[2];
8254  p[2] = r;
8255  p += 3;
8256  }
8257  nassertr(p <= image.p() + size, PTA_uchar());
8258  }
8259 
8260  return image;
8261 }
8262 
8263 /**
8264  * Called by read_dds for a DDS file in ABGR8 format.
8265  */
8266 PTA_uchar Texture::
8267 read_dds_level_abgr8(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
8268  // This is laid out in order R, G, B, A.
8269  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8270  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8271 
8272  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
8273  size_t row_bytes = x_size * 4;
8274  PTA_uchar image = PTA_uchar::empty_array(size);
8275  for (int y = y_size - 1; y >= 0; --y) {
8276  unsigned char *p = image.p() + y * row_bytes;
8277  in.read((char *)p, row_bytes);
8278 
8279  uint32_t *pw = (uint32_t *)p;
8280  for (int x = 0; x < x_size; ++x) {
8281  uint32_t w = *pw;
8282 #ifdef WORDS_BIGENDIAN
8283  // bigendian: convert R, G, B, A to B, G, R, A.
8284  w = ((w & 0xff00) << 16) | ((w & 0xff000000U) >> 16) | (w & 0xff00ff);
8285 #else
8286  // littendian: convert A, B, G, R to to A, R, G, B.
8287  w = ((w & 0xff) << 16) | ((w & 0xff0000) >> 16) | (w & 0xff00ff00U);
8288 #endif
8289  *pw = w;
8290  ++pw;
8291  }
8292  nassertr((unsigned char *)pw <= image.p() + size, PTA_uchar());
8293  }
8294 
8295  return image;
8296 }
8297 
8298 /**
8299  * Called by read_dds for a DDS file in RGBA8 format.
8300  */
8301 PTA_uchar Texture::
8302 read_dds_level_rgba8(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
8303  // This is actually laid out in order B, G, R, A.
8304  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8305  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8306 
8307  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
8308  size_t row_bytes = x_size * 4;
8309  PTA_uchar image = PTA_uchar::empty_array(size);
8310  for (int y = y_size - 1; y >= 0; --y) {
8311  unsigned char *p = image.p() + y * row_bytes;
8312  nassertr(p + row_bytes <= image.p() + size, PTA_uchar());
8313  in.read((char *)p, row_bytes);
8314  }
8315 
8316  return image;
8317 }
8318 
8319 /**
8320  * Called by read_dds for a DDS file in ABGR16 format.
8321  */
8322 PTA_uchar Texture::
8323 read_dds_level_abgr16(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
8324  // This is laid out in order R, G, B, A.
8325  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8326  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8327 
8328  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
8329  size_t row_bytes = x_size * 8;
8330  PTA_uchar image = PTA_uchar::empty_array(size);
8331  for (int y = y_size - 1; y >= 0; --y) {
8332  unsigned char *p = image.p() + y * row_bytes;
8333  in.read((char *)p, row_bytes);
8334 
8335  uint16_t *pw = (uint16_t *)p;
8336  for (int x = 0; x < x_size; ++x) {
8337  swap(pw[0], pw[2]);
8338  pw += 4;
8339  }
8340  nassertr((unsigned char *)pw <= image.p() + size, PTA_uchar());
8341  }
8342 
8343  return image;
8344 }
8345 
8346 /**
8347  * Called by read_dds for a DDS file in ABGR32 format.
8348  */
8349 PTA_uchar Texture::
8350 read_dds_level_abgr32(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
8351  // This is laid out in order R, G, B, A.
8352  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8353  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8354 
8355  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
8356  size_t row_bytes = x_size * 16;
8357  nassertr(row_bytes * y_size == size, PTA_uchar());
8358  PTA_uchar image = PTA_uchar::empty_array(size);
8359  for (int y = y_size - 1; y >= 0; --y) {
8360  unsigned char *p = image.p() + y * row_bytes;
8361  in.read((char *)p, row_bytes);
8362 
8363  uint32_t *pw = (uint32_t *)p;
8364  for (int x = 0; x < x_size; ++x) {
8365  swap(pw[0], pw[2]);
8366  pw += 4;
8367  }
8368  nassertr((unsigned char *)pw <= image.p() + size, PTA_uchar());
8369  }
8370 
8371  return image;
8372 }
8373 
8374 /**
8375  * Called by read_dds for a DDS file that needs no transformations applied.
8376  */
8377 PTA_uchar Texture::
8378 read_dds_level_raw(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
8379  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8380  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8381 
8382  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
8383  size_t row_bytes = x_size * cdata->_num_components * cdata->_component_width;
8384  nassertr(row_bytes * y_size == size, PTA_uchar());
8385  PTA_uchar image = PTA_uchar::empty_array(size);
8386  for (int y = y_size - 1; y >= 0; --y) {
8387  unsigned char *p = image.p() + y * row_bytes;
8388  in.read((char *)p, row_bytes);
8389  }
8390 
8391  return image;
8392 }
8393 
8394 /**
8395  * Called by read_dds for a DDS file whose format isn't one we've specifically
8396  * optimized.
8397  */
8398 PTA_uchar Texture::
8399 read_dds_level_generic_uncompressed(Texture *tex, CData *cdata, const DDSHeader &header,
8400  int n, istream &in) {
8401  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8402  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8403 
8404  int pitch = (x_size * header.pf.rgb_bitcount) / 8;
8405 
8406  // MS says the pitch can be supplied in the header file and must be DWORD
8407  // aligned, but this appears to apply to level 0 mipmaps only (where it
8408  // almost always will be anyway). Other mipmap levels seem to be tightly
8409  // packed, but there isn't a separate pitch for each mipmap level. Weird.
8410  if (n == 0) {
8411  pitch = ((pitch + 3) / 4) * 4;
8412  if (header.dds_flags & DDSD_PITCH) {
8413  pitch = header.pitch;
8414  }
8415  }
8416 
8417  int bpp = header.pf.rgb_bitcount / 8;
8418  int skip_bytes = pitch - (bpp * x_size);
8419  nassertr(skip_bytes >= 0, PTA_uchar());
8420 
8421  unsigned int r_mask = header.pf.r_mask;
8422  unsigned int g_mask = header.pf.g_mask;
8423  unsigned int b_mask = header.pf.b_mask;
8424  unsigned int a_mask = header.pf.a_mask;
8425 
8426  // Determine the number of bits to shift each mask to the right so that the
8427  // lowest on bit is at bit 0.
8428  int r_shift = get_lowest_on_bit(r_mask);
8429  int g_shift = get_lowest_on_bit(g_mask);
8430  int b_shift = get_lowest_on_bit(b_mask);
8431  int a_shift = get_lowest_on_bit(a_mask);
8432 
8433  // Then determine the scale factor required to raise the highest color value
8434  // to 0xff000000.
8435  unsigned int r_scale = 0;
8436  if (r_mask != 0) {
8437  r_scale = 0xff000000 / (r_mask >> r_shift);
8438  }
8439  unsigned int g_scale = 0;
8440  if (g_mask != 0) {
8441  g_scale = 0xff000000 / (g_mask >> g_shift);
8442  }
8443  unsigned int b_scale = 0;
8444  if (b_mask != 0) {
8445  b_scale = 0xff000000 / (b_mask >> b_shift);
8446  }
8447  unsigned int a_scale = 0;
8448  if (a_mask != 0) {
8449  a_scale = 0xff000000 / (a_mask >> a_shift);
8450  }
8451 
8452  bool add_alpha = has_alpha(cdata->_format);
8453 
8454  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
8455  size_t row_bytes = x_size * cdata->_num_components;
8456  PTA_uchar image = PTA_uchar::empty_array(size);
8457  for (int y = y_size - 1; y >= 0; --y) {
8458  unsigned char *p = image.p() + y * row_bytes;
8459  for (int x = 0; x < x_size; ++x) {
8460 
8461  // Read a little-endian numeric value of bpp bytes.
8462  unsigned int pixel = 0;
8463  int shift = 0;
8464  for (int bi = 0; bi < bpp; ++bi) {
8465  unsigned int ch = (unsigned char)in.get();
8466  pixel |= (ch << shift);
8467  shift += 8;
8468  }
8469 
8470  // Then break apart that value into its R, G, B, and maybe A components.
8471  unsigned int r = (((pixel & r_mask) >> r_shift) * r_scale) >> 24;
8472  unsigned int g = (((pixel & g_mask) >> g_shift) * g_scale) >> 24;
8473  unsigned int b = (((pixel & b_mask) >> b_shift) * b_scale) >> 24;
8474 
8475  // Store the components in the Texture's image data.
8476  store_unscaled_byte(p, b);
8477  store_unscaled_byte(p, g);
8478  store_unscaled_byte(p, r);
8479  if (add_alpha) {
8480  unsigned int a = (((pixel & a_mask) >> a_shift) * a_scale) >> 24;
8481  store_unscaled_byte(p, a);
8482  }
8483  }
8484  nassertr(p <= image.p() + size, PTA_uchar());
8485  for (int bi = 0; bi < skip_bytes; ++bi) {
8486  in.get();
8487  }
8488  }
8489 
8490  return image;
8491 }
8492 
8493 /**
8494  * Called by read_dds for a DDS file in uncompressed luminance or luminance-
8495  * alpha format.
8496  */
8497 PTA_uchar Texture::
8498 read_dds_level_luminance_uncompressed(Texture *tex, CData *cdata, const DDSHeader &header,
8499  int n, istream &in) {
8500  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8501  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8502 
8503  int pitch = (x_size * header.pf.rgb_bitcount) / 8;
8504 
8505  // MS says the pitch can be supplied in the header file and must be DWORD
8506  // aligned, but this appears to apply to level 0 mipmaps only (where it
8507  // almost always will be anyway). Other mipmap levels seem to be tightly
8508  // packed, but there isn't a separate pitch for each mipmap level. Weird.
8509  if (n == 0) {
8510  pitch = ((pitch + 3) / 4) * 4;
8511  if (header.dds_flags & DDSD_PITCH) {
8512  pitch = header.pitch;
8513  }
8514  }
8515 
8516  int bpp = header.pf.rgb_bitcount / 8;
8517  int skip_bytes = pitch - (bpp * x_size);
8518  nassertr(skip_bytes >= 0, PTA_uchar());
8519 
8520  unsigned int r_mask = header.pf.r_mask;
8521  unsigned int a_mask = header.pf.a_mask;
8522 
8523  // Determine the number of bits to shift each mask to the right so that the
8524  // lowest on bit is at bit 0.
8525  int r_shift = get_lowest_on_bit(r_mask);
8526  int a_shift = get_lowest_on_bit(a_mask);
8527 
8528  // Then determine the scale factor required to raise the highest color value
8529  // to 0xff000000.
8530  unsigned int r_scale = 0;
8531  if (r_mask != 0) {
8532  r_scale = 0xff000000 / (r_mask >> r_shift);
8533  }
8534  unsigned int a_scale = 0;
8535  if (a_mask != 0) {
8536  a_scale = 0xff000000 / (a_mask >> a_shift);
8537  }
8538 
8539  bool add_alpha = has_alpha(cdata->_format);
8540 
8541  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
8542  size_t row_bytes = x_size * cdata->_num_components;
8543  PTA_uchar image = PTA_uchar::empty_array(size);
8544  for (int y = y_size - 1; y >= 0; --y) {
8545  unsigned char *p = image.p() + y * row_bytes;
8546  for (int x = 0; x < x_size; ++x) {
8547 
8548  // Read a little-endian numeric value of bpp bytes.
8549  unsigned int pixel = 0;
8550  int shift = 0;
8551  for (int bi = 0; bi < bpp; ++bi) {
8552  unsigned int ch = (unsigned char)in.get();
8553  pixel |= (ch << shift);
8554  shift += 8;
8555  }
8556 
8557  unsigned int r = (((pixel & r_mask) >> r_shift) * r_scale) >> 24;
8558 
8559  // Store the components in the Texture's image data.
8560  store_unscaled_byte(p, r);
8561  if (add_alpha) {
8562  unsigned int a = (((pixel & a_mask) >> a_shift) * a_scale) >> 24;
8563  store_unscaled_byte(p, a);
8564  }
8565  }
8566  nassertr(p <= image.p() + size, PTA_uchar());
8567  for (int bi = 0; bi < skip_bytes; ++bi) {
8568  in.get();
8569  }
8570  }
8571 
8572  return image;
8573 }
8574 
8575 /**
8576  * Called by read_dds for DXT1 file format.
8577  */
8578 PTA_uchar Texture::
8579 read_dds_level_bc1(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
8580  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8581  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8582 
8583  static const int div = 4;
8584  static const int block_bytes = 8;
8585 
8586  // The DXT1 image is divided into num_rows x num_cols blocks, where each
8587  // block represents 4x4 pixels.
8588  int num_cols = max(div, x_size) / div;
8589  int num_rows = max(div, y_size) / div;
8590  int row_length = num_cols * block_bytes;
8591  int linear_size = row_length * num_rows;
8592 
8593  if (n == 0) {
8594  if (header.dds_flags & DDSD_LINEARSIZE) {
8595  nassertr(linear_size == (int)header.pitch, PTA_uchar());
8596  }
8597  }
8598 
8599  PTA_uchar image = PTA_uchar::empty_array(linear_size);
8600 
8601  if (y_size >= 4) {
8602  // We have to flip the image as we read it, because of DirectX's inverted
8603  // sense of up. That means we (a) reverse the order of the rows of blocks
8604  // . . .
8605  for (int ri = num_rows - 1; ri >= 0; --ri) {
8606  unsigned char *p = image.p() + row_length * ri;
8607  in.read((char *)p, row_length);
8608 
8609  for (int ci = 0; ci < num_cols; ++ci) {
8610  // . . . and (b) within each block, we reverse the 4 individual rows
8611  // of 4 pixels.
8612  uint32_t *cells = (uint32_t *)p;
8613  uint32_t w = cells[1];
8614  w = ((w & 0xff) << 24) | ((w & 0xff00) << 8) | ((w & 0xff0000) >> 8) | ((w & 0xff000000U) >> 24);
8615  cells[1] = w;
8616 
8617  p += block_bytes;
8618  }
8619  }
8620 
8621  } else if (y_size >= 2) {
8622  // To invert a two-pixel high image, we just flip two rows within a cell.
8623  unsigned char *p = image.p();
8624  in.read((char *)p, row_length);
8625 
8626  for (int ci = 0; ci < num_cols; ++ci) {
8627  uint32_t *cells = (uint32_t *)p;
8628  uint32_t w = cells[1];
8629  w = ((w & 0xff) << 8) | ((w & 0xff00) >> 8);
8630  cells[1] = w;
8631 
8632  p += block_bytes;
8633  }
8634 
8635  } else if (y_size >= 1) {
8636  // No need to invert a one-pixel-high image.
8637  unsigned char *p = image.p();
8638  in.read((char *)p, row_length);
8639  }
8640 
8641  return image;
8642 }
8643 
8644 /**
8645  * Called by read_dds for DXT2 or DXT3 file format.
8646  */
8647 PTA_uchar Texture::
8648 read_dds_level_bc2(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
8649  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8650  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8651 
8652  static const int div = 4;
8653  static const int block_bytes = 16;
8654 
8655  // The DXT3 image is divided into num_rows x num_cols blocks, where each
8656  // block represents 4x4 pixels. Unlike DXT1, each block consists of two
8657  // 8-byte chunks, representing the alpha and color separately.
8658  int num_cols = max(div, x_size) / div;
8659  int num_rows = max(div, y_size) / div;
8660  int row_length = num_cols * block_bytes;
8661  int linear_size = row_length * num_rows;
8662 
8663  if (n == 0) {
8664  if (header.dds_flags & DDSD_LINEARSIZE) {
8665  nassertr(linear_size == (int)header.pitch, PTA_uchar());
8666  }
8667  }
8668 
8669  PTA_uchar image = PTA_uchar::empty_array(linear_size);
8670 
8671  if (y_size >= 4) {
8672  // We have to flip the image as we read it, because of DirectX's inverted
8673  // sense of up. That means we (a) reverse the order of the rows of blocks
8674  // . . .
8675  for (int ri = num_rows - 1; ri >= 0; --ri) {
8676  unsigned char *p = image.p() + row_length * ri;
8677  in.read((char *)p, row_length);
8678 
8679  for (int ci = 0; ci < num_cols; ++ci) {
8680  // . . . and (b) within each block, we reverse the 4 individual rows
8681  // of 4 pixels.
8682  uint32_t *cells = (uint32_t *)p;
8683 
8684  // Alpha. The block is four 16-bit words of pixel data.
8685  uint32_t w0 = cells[0];
8686  uint32_t w1 = cells[1];
8687  w0 = ((w0 & 0xffff) << 16) | ((w0 & 0xffff0000U) >> 16);
8688  w1 = ((w1 & 0xffff) << 16) | ((w1 & 0xffff0000U) >> 16);
8689  cells[0] = w1;
8690  cells[1] = w0;
8691 
8692  // Color. Only the second 32-bit dword of the color block represents
8693  // the pixel data.
8694  uint32_t w = cells[3];
8695  w = ((w & 0xff) << 24) | ((w & 0xff00) << 8) | ((w & 0xff0000) >> 8) | ((w & 0xff000000U) >> 24);
8696  cells[3] = w;
8697 
8698  p += block_bytes;
8699  }
8700  }
8701 
8702  } else if (y_size >= 2) {
8703  // To invert a two-pixel high image, we just flip two rows within a cell.
8704  unsigned char *p = image.p();
8705  in.read((char *)p, row_length);
8706 
8707  for (int ci = 0; ci < num_cols; ++ci) {
8708  uint32_t *cells = (uint32_t *)p;
8709 
8710  uint32_t w0 = cells[0];
8711  w0 = ((w0 & 0xffff) << 16) | ((w0 & 0xffff0000U) >> 16);
8712  cells[0] = w0;
8713 
8714  uint32_t w = cells[3];
8715  w = ((w & 0xff) << 8) | ((w & 0xff00) >> 8);
8716  cells[3] = w;
8717 
8718  p += block_bytes;
8719  }
8720 
8721  } else if (y_size >= 1) {
8722  // No need to invert a one-pixel-high image.
8723  unsigned char *p = image.p();
8724  in.read((char *)p, row_length);
8725  }
8726 
8727  return image;
8728 }
8729 
8730 /**
8731  * Called by read_dds for DXT4 or DXT5 file format.
8732  */
8733 PTA_uchar Texture::
8734 read_dds_level_bc3(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
8735  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8736  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8737 
8738  static const int div = 4;
8739  static const int block_bytes = 16;
8740 
8741  // The DXT5 image is similar to DXT3, in that there each 4x4 block of pixels
8742  // consists of an alpha block and a color block, but the layout of the alpha
8743  // block is different.
8744  int num_cols = max(div, x_size) / div;
8745  int num_rows = max(div, y_size) / div;
8746  int row_length = num_cols * block_bytes;
8747  int linear_size = row_length * num_rows;
8748 
8749  if (n == 0) {
8750  if (header.dds_flags & DDSD_LINEARSIZE) {
8751  nassertr(linear_size == (int)header.pitch, PTA_uchar());
8752  }
8753  }
8754 
8755  PTA_uchar image = PTA_uchar::empty_array(linear_size);
8756 
8757  if (y_size >= 4) {
8758  // We have to flip the image as we read it, because of DirectX's inverted
8759  // sense of up. That means we (a) reverse the order of the rows of blocks
8760  // . . .
8761  for (int ri = num_rows - 1; ri >= 0; --ri) {
8762  unsigned char *p = image.p() + row_length * ri;
8763  in.read((char *)p, row_length);
8764 
8765  for (int ci = 0; ci < num_cols; ++ci) {
8766  // . . . and (b) within each block, we reverse the 4 individual rows
8767  // of 4 pixels.
8768  uint32_t *cells = (uint32_t *)p;
8769 
8770  // Alpha. The block is one 16-bit word of reference values, followed
8771  // by six words of pixel values, in 12-bit rows. Tricky to invert.
8772  unsigned char p2 = p[2];
8773  unsigned char p3 = p[3];
8774  unsigned char p4 = p[4];
8775  unsigned char p5 = p[5];
8776  unsigned char p6 = p[6];
8777  unsigned char p7 = p[7];
8778 
8779  p[2] = ((p7 & 0xf) << 4) | ((p6 & 0xf0) >> 4);
8780  p[3] = ((p5 & 0xf) << 4) | ((p7 & 0xf0) >> 4);
8781  p[4] = ((p6 & 0xf) << 4) | ((p5 & 0xf0) >> 4);
8782  p[5] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4);
8783  p[6] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4);
8784  p[7] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4);
8785 
8786  // Color. Only the second 32-bit dword of the color block represents
8787  // the pixel data.
8788  uint32_t w = cells[3];
8789  w = ((w & 0xff) << 24) | ((w & 0xff00) << 8) | ((w & 0xff0000) >> 8) | ((w & 0xff000000U) >> 24);
8790  cells[3] = w;
8791 
8792  p += block_bytes;
8793  }
8794  }
8795 
8796  } else if (y_size >= 2) {
8797  // To invert a two-pixel high image, we just flip two rows within a cell.
8798  unsigned char *p = image.p();
8799  in.read((char *)p, row_length);
8800 
8801  for (int ci = 0; ci < num_cols; ++ci) {
8802  uint32_t *cells = (uint32_t *)p;
8803 
8804  unsigned char p2 = p[2];
8805  unsigned char p3 = p[3];
8806  unsigned char p4 = p[4];
8807 
8808  p[2] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4);
8809  p[3] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4);
8810  p[4] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4);
8811 
8812  uint32_t w0 = cells[0];
8813  w0 = ((w0 & 0xffff) << 16) | ((w0 & 0xffff0000U) >> 16);
8814  cells[0] = w0;
8815 
8816  uint32_t w = cells[3];
8817  w = ((w & 0xff) << 8) | ((w & 0xff00) >> 8);
8818  cells[3] = w;
8819 
8820  p += block_bytes;
8821  }
8822 
8823  } else if (y_size >= 1) {
8824  // No need to invert a one-pixel-high image.
8825  unsigned char *p = image.p();
8826  in.read((char *)p, row_length);
8827  }
8828 
8829  return image;
8830 }
8831 
8832 /**
8833  * Called by read_dds for ATI1 compression.
8834  */
8835 PTA_uchar Texture::
8836 read_dds_level_bc4(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
8837  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8838  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8839 
8840  static const int div = 4;
8841  static const int block_bytes = 8;
8842 
8843  // The ATI1 (BC4) format uses the same compression mechanism as the alpha
8844  // channel of DXT5.
8845  int num_cols = max(div, x_size) / div;
8846  int num_rows = max(div, y_size) / div;
8847  int row_length = num_cols * block_bytes;
8848  int linear_size = row_length * num_rows;
8849 
8850  if (n == 0) {
8851  if (header.dds_flags & DDSD_LINEARSIZE) {
8852  nassertr(linear_size == (int)header.pitch, PTA_uchar());
8853  }
8854  }
8855 
8856  PTA_uchar image = PTA_uchar::empty_array(linear_size);
8857 
8858  if (y_size >= 4) {
8859  // We have to flip the image as we read it, because of DirectX's inverted
8860  // sense of up. That means we (a) reverse the order of the rows of blocks
8861  // . . .
8862  for (int ri = num_rows - 1; ri >= 0; --ri) {
8863  unsigned char *p = image.p() + row_length * ri;
8864  in.read((char *)p, row_length);
8865 
8866  for (int ci = 0; ci < num_cols; ++ci) {
8867  // . . . and (b) within each block, we reverse the 4 individual rows
8868  // of 4 pixels. The block is one 16-bit word of reference values,
8869  // followed by six words of pixel values, in 12-bit rows. Tricky to
8870  // invert.
8871  unsigned char p2 = p[2];
8872  unsigned char p3 = p[3];
8873  unsigned char p4 = p[4];
8874  unsigned char p5 = p[5];
8875  unsigned char p6 = p[6];
8876  unsigned char p7 = p[7];
8877 
8878  p[2] = ((p7 & 0xf) << 4) | ((p6 & 0xf0) >> 4);
8879  p[3] = ((p5 & 0xf) << 4) | ((p7 & 0xf0) >> 4);
8880  p[4] = ((p6 & 0xf) << 4) | ((p5 & 0xf0) >> 4);
8881  p[5] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4);
8882  p[6] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4);
8883  p[7] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4);
8884 
8885  p += block_bytes;
8886  }
8887  }
8888 
8889  } else if (y_size >= 2) {
8890  // To invert a two-pixel high image, we just flip two rows within a cell.
8891  unsigned char *p = image.p();
8892  in.read((char *)p, row_length);
8893 
8894  for (int ci = 0; ci < num_cols; ++ci) {
8895  unsigned char p2 = p[2];
8896  unsigned char p3 = p[3];
8897  unsigned char p4 = p[4];
8898 
8899  p[2] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4);
8900  p[3] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4);
8901  p[4] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4);
8902 
8903  p += block_bytes;
8904  }
8905 
8906  } else if (y_size >= 1) {
8907  // No need to invert a one-pixel-high image.
8908  unsigned char *p = image.p();
8909  in.read((char *)p, row_length);
8910  }
8911 
8912  return image;
8913 }
8914 
8915 /**
8916  * Called by read_dds for ATI2 compression.
8917  */
8918 PTA_uchar Texture::
8919 read_dds_level_bc5(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
8920  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
8921  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
8922 
8923  // The ATI2 (BC5) format uses the same compression mechanism as the ATI1
8924  // (BC4) format, but doubles the channels.
8925  int num_cols = max(4, x_size) / 2;
8926  int num_rows = max(4, y_size) / 4;
8927  int row_length = num_cols * 8;
8928  int linear_size = row_length * num_rows;
8929 
8930  if (n == 0) {
8931  if (header.dds_flags & DDSD_LINEARSIZE) {
8932  nassertr(linear_size == (int)header.pitch, PTA_uchar());
8933  }
8934  }
8935 
8936  PTA_uchar image = PTA_uchar::empty_array(linear_size);
8937 
8938  if (y_size >= 4) {
8939  // We have to flip the image as we read it, because of DirectX's inverted
8940  // sense of up. That means we (a) reverse the order of the rows of blocks
8941  // . . .
8942  for (int ri = num_rows - 1; ri >= 0; --ri) {
8943  unsigned char *p = image.p() + row_length * ri;
8944  in.read((char *)p, row_length);
8945 
8946  for (int ci = 0; ci < num_cols; ++ci) {
8947  // . . . and (b) within each block, we reverse the 4 individual rows
8948  // of 4 pixels. The block is one 16-bit word of reference values,
8949  // followed by six words of pixel values, in 12-bit rows. Tricky to
8950  // invert.
8951  unsigned char p2 = p[2];
8952  unsigned char p3 = p[3];
8953  unsigned char p4 = p[4];
8954  unsigned char p5 = p[5];
8955  unsigned char p6 = p[6];
8956  unsigned char p7 = p[7];
8957 
8958  p[2] = ((p7 & 0xf) << 4) | ((p6 & 0xf0) >> 4);
8959  p[3] = ((p5 & 0xf) << 4) | ((p7 & 0xf0) >> 4);
8960  p[4] = ((p6 & 0xf) << 4) | ((p5 & 0xf0) >> 4);
8961  p[5] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4);
8962  p[6] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4);
8963  p[7] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4);
8964 
8965  p += 8;
8966  }
8967  }
8968 
8969  } else if (y_size >= 2) {
8970  // To invert a two-pixel high image, we just flip two rows within a cell.
8971  unsigned char *p = image.p();
8972  in.read((char *)p, row_length);
8973 
8974  for (int ci = 0; ci < num_cols; ++ci) {
8975  unsigned char p2 = p[2];
8976  unsigned char p3 = p[3];
8977  unsigned char p4 = p[4];
8978 
8979  p[2] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4);
8980  p[3] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4);
8981  p[4] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4);
8982 
8983  p += 8;
8984  }
8985 
8986  } else if (y_size >= 1) {
8987  // No need to invert a one-pixel-high image.
8988  unsigned char *p = image.p();
8989  in.read((char *)p, row_length);
8990  }
8991 
8992  return image;
8993 }
8994 
8995 /**
8996  * Removes the indicated PreparedGraphicsObjects table from the Texture's
8997  * table, without actually releasing the texture. This is intended to be
8998  * called only from PreparedGraphicsObjects::release_texture(); it should
8999  * never be called by user code.
9000  */
9001 void Texture::
9002 clear_prepared(int view, PreparedGraphicsObjects *prepared_objects) {
9003  PreparedViews::iterator pvi;
9004  pvi = _prepared_views.find(prepared_objects);
9005  if (pvi != _prepared_views.end()) {
9006  Contexts &contexts = (*pvi).second;
9007  Contexts::iterator ci;
9008  ci = contexts.find(view);
9009  if (ci != contexts.end()) {
9010  contexts.erase(ci);
9011  }
9012 
9013  if (contexts.empty()) {
9014  _prepared_views.erase(pvi);
9015  }
9016  }
9017 }
9018 
9019 /**
9020  * Reduces the number of channels in the texture, if necessary, according to
9021  * num_channels.
9022  */
9023 void Texture::
9024 consider_downgrade(PNMImage &pnmimage, int num_channels, const string &name) {
9025  if (num_channels != 0 && num_channels < pnmimage.get_num_channels()) {
9026  // One special case: we can't reduce from 3 to 2 components, since that
9027  // would require adding an alpha channel.
9028  if (pnmimage.get_num_channels() == 3 && num_channels == 2) {
9029  return;
9030  }
9031 
9032  gobj_cat.info()
9033  << "Downgrading " << name << " from "
9034  << pnmimage.get_num_channels() << " components to "
9035  << num_channels << ".\n";
9036  pnmimage.set_num_channels(num_channels);
9037  }
9038 }
9039 
9040 /**
9041  * Called by generate_simple_ram_image(), this compares the two PNMImages
9042  * pixel-by-pixel. If they're similar enough (within a given threshold),
9043  * returns true.
9044  */
9045 bool Texture::
9046 compare_images(const PNMImage &a, const PNMImage &b) {
9047  nassertr(a.get_maxval() == 255 && b.get_maxval() == 255, false);
9048  nassertr(a.get_num_channels() == 4 && b.get_num_channels() == 4, false);
9049  nassertr(a.get_x_size() == b.get_x_size() &&
9050  a.get_y_size() == b.get_y_size(), false);
9051 
9052  const xel *a_array = a.get_array();
9053  const xel *b_array = b.get_array();
9054  const xelval *a_alpha = a.get_alpha_array();
9055  const xelval *b_alpha = b.get_alpha_array();
9056 
9057  int x_size = a.get_x_size();
9058 
9059  int delta = 0;
9060  for (int yi = 0; yi < a.get_y_size(); ++yi) {
9061  const xel *a_row = a_array + yi * x_size;
9062  const xel *b_row = b_array + yi * x_size;
9063  const xelval *a_alpha_row = a_alpha + yi * x_size;
9064  const xelval *b_alpha_row = b_alpha + yi * x_size;
9065  for (int xi = 0; xi < x_size; ++xi) {
9066  delta += abs(PPM_GETR(a_row[xi]) - PPM_GETR(b_row[xi]));
9067  delta += abs(PPM_GETG(a_row[xi]) - PPM_GETG(b_row[xi]));
9068  delta += abs(PPM_GETB(a_row[xi]) - PPM_GETB(b_row[xi]));
9069  delta += abs(a_alpha_row[xi] - b_alpha_row[xi]);
9070  }
9071  }
9072 
9073  double average_delta = (double)delta / ((double)a.get_x_size() * (double)b.get_y_size() * (double)a.get_maxval());
9074  return (average_delta <= simple_image_threshold);
9075 }
9076 
9077 /**
9078  * Generates the next mipmap level from the previous one. If there are
9079  * multiple pages (e.g. a cube map), generates each page independently.
9080  *
9081  * x_size and y_size are the size of the previous level. They need not be a
9082  * power of 2, or even a multiple of 2.
9083  *
9084  * Assumes the lock is already held.
9085  */
9086 void Texture::
9087 do_filter_2d_mipmap_pages(const CData *cdata,
9088  Texture::RamImage &to, const Texture::RamImage &from,
9089  int x_size, int y_size) const {
9090  Filter2DComponent *filter_component;
9091  Filter2DComponent *filter_alpha;
9092 
9093  if (is_srgb(cdata->_format)) {
9094  // We currently only support sRGB mipmap generation for unsigned byte
9095  // textures, due to our use of a lookup table.
9096  nassertv(cdata->_component_type == T_unsigned_byte);
9097 
9098  if (has_sse2_sRGB_encode()) {
9099  filter_component = &filter_2d_unsigned_byte_srgb_sse2;
9100  } else {
9101  filter_component = &filter_2d_unsigned_byte_srgb;
9102  }
9103 
9104  // Alpha is always linear.
9105  filter_alpha = &filter_2d_unsigned_byte;
9106 
9107  } else {
9108  switch (cdata->_component_type) {
9109  case T_unsigned_byte:
9110  filter_component = &filter_2d_unsigned_byte;
9111  break;
9112 
9113  case T_unsigned_short:
9114  filter_component = &filter_2d_unsigned_short;
9115  break;
9116 
9117  case T_float:
9118  filter_component = &filter_2d_float;
9119  break;
9120 
9121  default:
9122  gobj_cat.error()
9123  << "Unable to generate mipmaps for 2D texture with component type "
9124  << cdata->_component_type << "!";
9125  return;
9126  }
9127  filter_alpha = filter_component;
9128  }
9129 
9130  size_t pixel_size = cdata->_num_components * cdata->_component_width;
9131  size_t row_size = (size_t)x_size * pixel_size;
9132 
9133  int to_x_size = max(x_size >> 1, 1);
9134  int to_y_size = max(y_size >> 1, 1);
9135 
9136  size_t to_row_size = (size_t)to_x_size * pixel_size;
9137  to._page_size = (size_t)to_y_size * to_row_size;
9138  to._image = PTA_uchar::empty_array(to._page_size * cdata->_z_size * cdata->_num_views, get_class_type());
9139 
9140  bool alpha = has_alpha(cdata->_format);
9141  int num_color_components = cdata->_num_components;
9142  if (alpha) {
9143  --num_color_components;
9144  }
9145 
9146  int num_pages = cdata->_z_size * cdata->_num_views;
9147  for (int z = 0; z < num_pages; ++z) {
9148  // For each level.
9149  unsigned char *p = to._image.p() + z * to._page_size;
9150  nassertv(p <= to._image.p() + to._image.size() + to._page_size);
9151  const unsigned char *q = from._image.p() + z * from._page_size;
9152  nassertv(q <= from._image.p() + from._image.size() + from._page_size);
9153  if (y_size != 1) {
9154  int y;
9155  for (y = 0; y < y_size - 1; y += 2) {
9156  // For each row.
9157  nassertv(p == to._image.p() + z * to._page_size + (y / 2) * to_row_size);
9158  nassertv(q == from._image.p() + z * from._page_size + y * row_size);
9159  if (x_size != 1) {
9160  int x;
9161  for (x = 0; x < x_size - 1; x += 2) {
9162  // For each pixel.
9163  for (int c = 0; c < num_color_components; ++c) {
9164  // For each component.
9165  filter_component(p, q, pixel_size, row_size);
9166  }
9167  if (alpha) {
9168  filter_alpha(p, q, pixel_size, row_size);
9169  }
9170  q += pixel_size;
9171  }
9172  if (x < x_size) {
9173  // Skip the last odd pixel.
9174  q += pixel_size;
9175  }
9176  } else {
9177  // Just one pixel.
9178  for (int c = 0; c < num_color_components; ++c) {
9179  // For each component.
9180  filter_component(p, q, 0, row_size);
9181  }
9182  if (alpha) {
9183  filter_alpha(p, q, 0, row_size);
9184  }
9185  }
9186  q += row_size;
9188  }
9189  if (y < y_size) {
9190  // Skip the last odd row.
9191  q += row_size;
9192  }
9193  } else {
9194  // Just one row.
9195  if (x_size != 1) {
9196  int x;
9197  for (x = 0; x < x_size - 1; x += 2) {
9198  // For each pixel.
9199  for (int c = 0; c < num_color_components; ++c) {
9200  // For each component.
9201  filter_component(p, q, pixel_size, 0);
9202  }
9203  if (alpha) {
9204  filter_alpha(p, q, pixel_size, 0);
9205  }
9206  q += pixel_size;
9207  }
9208  if (x < x_size) {
9209  // Skip the last odd pixel.
9210  q += pixel_size;
9211  }
9212  } else {
9213  // Just one pixel.
9214  for (int c = 0; c < num_color_components; ++c) {
9215  // For each component.
9216  filter_component(p, q, 0, 0);
9217  }
9218  if (alpha) {
9219  filter_alpha(p, q, pixel_size, 0);
9220  }
9221  }
9222  }
9223 
9224  nassertv(p == to._image.p() + (z + 1) * to._page_size);
9225  nassertv(q == from._image.p() + (z + 1) * from._page_size);
9226  }
9227 }
9228 
9229 /**
9230  * Generates the next mipmap level from the previous one, treating all the
9231  * pages of the level as a single 3-d block of pixels.
9232  *
9233  * x_size, y_size, and z_size are the size of the previous level. They need
9234  * not be a power of 2, or even a multiple of 2.
9235  *
9236  * Assumes the lock is already held.
9237  */
9238 void Texture::
9239 do_filter_3d_mipmap_level(const CData *cdata,
9240  Texture::RamImage &to, const Texture::RamImage &from,
9241  int x_size, int y_size, int z_size) const {
9242  Filter3DComponent *filter_component;
9243  Filter3DComponent *filter_alpha;
9244 
9245  if (is_srgb(cdata->_format)) {
9246  // We currently only support sRGB mipmap generation for unsigned byte
9247  // textures, due to our use of a lookup table.
9248  nassertv(cdata->_component_type == T_unsigned_byte);
9249 
9250  if (has_sse2_sRGB_encode()) {
9251  filter_component = &filter_3d_unsigned_byte_srgb_sse2;
9252  } else {
9253  filter_component = &filter_3d_unsigned_byte_srgb;
9254  }
9255 
9256  // Alpha is always linear.
9257  filter_alpha = &filter_3d_unsigned_byte;
9258 
9259  } else {
9260  switch (cdata->_component_type) {
9261  case T_unsigned_byte:
9262  filter_component = &filter_3d_unsigned_byte;
9263  break;
9264 
9265  case T_unsigned_short:
9266  filter_component = &filter_3d_unsigned_short;
9267  break;
9268 
9269  case T_float:
9270  filter_component = &filter_3d_float;
9271  break;
9272 
9273  default:
9274  gobj_cat.error()
9275  << "Unable to generate mipmaps for 3D texture with component type "
9276  << cdata->_component_type << "!";
9277  return;
9278  }
9279  filter_alpha = filter_component;
9280  }
9281 
9282  size_t pixel_size = cdata->_num_components * cdata->_component_width;
9283  size_t row_size = (size_t)x_size * pixel_size;
9284  size_t page_size = (size_t)y_size * row_size;
9285  size_t view_size = (size_t)z_size * page_size;
9286 
9287  int to_x_size = max(x_size >> 1, 1);
9288  int to_y_size = max(y_size >> 1, 1);
9289  int to_z_size = max(z_size >> 1, 1);
9290 
9291  size_t to_row_size = (size_t)to_x_size * pixel_size;
9292  size_t to_page_size = (size_t)to_y_size * to_row_size;
9293  size_t to_view_size = (size_t)to_z_size * to_page_size;
9294  to._page_size = to_page_size;
9295  to._image = PTA_uchar::empty_array(to_page_size * to_z_size * cdata->_num_views, get_class_type());
9296 
9297  bool alpha = has_alpha(cdata->_format);
9298  int num_color_components = cdata->_num_components;
9299  if (alpha) {
9300  --num_color_components;
9301  }
9302 
9303  for (int view = 0; view < cdata->_num_views; ++view) {
9304  unsigned char *start_to = to._image.p() + view * to_view_size;
9305  const unsigned char *start_from = from._image.p() + view * view_size;
9306  nassertv(start_to + to_view_size <= to._image.p() + to._image.size());
9307  nassertv(start_from + view_size <= from._image.p() + from._image.size());
9308  unsigned char *p = start_to;
9309  const unsigned char *q = start_from;
9310  if (z_size != 1) {
9311  int z;
9312  for (z = 0; z < z_size - 1; z += 2) {
9313  // For each level.
9314  nassertv(p == start_to + (z / 2) * to_page_size);
9315  nassertv(q == start_from + z * page_size);
9316  if (y_size != 1) {
9317  int y;
9318  for (y = 0; y < y_size - 1; y += 2) {
9319  // For each row.
9320  nassertv(p == start_to + (z / 2) * to_page_size + (y / 2) * to_row_size);
9321  nassertv(q == start_from + z * page_size + y * row_size);
9322  if (x_size != 1) {
9323  int x;
9324  for (x = 0; x < x_size - 1; x += 2) {
9325  // For each pixel.
9326  for (int c = 0; c < num_color_components; ++c) {
9327  // For each component.
9328  filter_component(p, q, pixel_size, row_size, page_size);
9329  }
9330  if (alpha) {
9331  filter_alpha(p, q, pixel_size, row_size, page_size);
9332  }
9333  q += pixel_size;
9334  }
9335  if (x < x_size) {
9336  // Skip the last odd pixel.
9337  q += pixel_size;
9338  }
9339  } else {
9340  // Just one pixel.
9341  for (int c = 0; c < num_color_components; ++c) {
9342  // For each component.
9343  filter_component(p, q, 0, row_size, page_size);
9344  }
9345  if (alpha) {
9346  filter_alpha(p, q, 0, row_size, page_size);
9347  }
9348  }
9349  q += row_size;
9351  }
9352  if (y < y_size) {
9353  // Skip the last odd row.
9354  q += row_size;
9355  }
9356  } else {
9357  // Just one row.
9358  if (x_size != 1) {
9359  int x;
9360  for (x = 0; x < x_size - 1; x += 2) {
9361  // For each pixel.
9362  for (int c = 0; c < num_color_components; ++c) {
9363  // For each component.
9364  filter_component(p, q, pixel_size, 0, page_size);
9365  }
9366  if (alpha) {
9367  filter_alpha(p, q, pixel_size, 0, page_size);
9368  }
9369  q += pixel_size;
9370  }
9371  if (x < x_size) {
9372  // Skip the last odd pixel.
9373  q += pixel_size;
9374  }
9375  } else {
9376  // Just one pixel.
9377  for (int c = 0; c < num_color_components; ++c) {
9378  // For each component.
9379  filter_component(p, q, 0, 0, page_size);
9380  }
9381  if (alpha) {
9382  filter_alpha(p, q, 0, 0, page_size);
9383  }
9384  }
9385  }
9386  q += page_size;
9387  }
9388  if (z < z_size) {
9389  // Skip the last odd page.
9390  q += page_size;
9391  }
9392  } else {
9393  // Just one page.
9394  if (y_size != 1) {
9395  int y;
9396  for (y = 0; y < y_size - 1; y += 2) {
9397  // For each row.
9398  nassertv(p == start_to + (y / 2) * to_row_size);
9399  nassertv(q == start_from + y * row_size);
9400  if (x_size != 1) {
9401  int x;
9402  for (x = 0; x < x_size - 1; x += 2) {
9403  // For each pixel.
9404  for (int c = 0; c < num_color_components; ++c) {
9405  // For each component.
9406  filter_component(p, q, pixel_size, row_size, 0);
9407  }
9408  if (alpha) {
9409  filter_alpha(p, q, pixel_size, row_size, 0);
9410  }
9411  q += pixel_size;
9412  }
9413  if (x < x_size) {
9414  // Skip the last odd pixel.
9415  q += pixel_size;
9416  }
9417  } else {
9418  // Just one pixel.
9419  for (int c = 0; c < num_color_components; ++c) {
9420  // For each component.
9421  filter_component(p, q, 0, row_size, 0);
9422  }
9423  if (alpha) {
9424  filter_alpha(p, q, 0, row_size, 0);
9425  }
9426  }
9427  q += row_size;
9429  }
9430  if (y < y_size) {
9431  // Skip the last odd row.
9432  q += row_size;
9433  }
9434  } else {
9435  // Just one row.
9436  if (x_size != 1) {
9437  int x;
9438  for (x = 0; x < x_size - 1; x += 2) {
9439  // For each pixel.
9440  for (int c = 0; c < num_color_components; ++c) {
9441  // For each component.
9442  filter_component(p, q, pixel_size, 0, 0);
9443  }
9444  if (alpha) {
9445  filter_alpha(p, q, pixel_size, 0, 0);
9446  }
9447  q += pixel_size;
9448  }
9449  if (x < x_size) {
9450  // Skip the last odd pixel.
9451  q += pixel_size;
9452  }
9453  } else {
9454  // Just one pixel.
9455  for (int c = 0; c < num_color_components; ++c) {
9456  // For each component.
9457  filter_component(p, q, 0, 0, 0);
9458  }
9459  if (alpha) {
9460  filter_alpha(p, q, 0, 0, 0);
9461  }
9462  }
9463  }
9464  }
9465 
9466  nassertv(p == start_to + to_z_size * to_page_size);
9467  nassertv(q == start_from + z_size * page_size);
9468  }
9469 }
9470 
9471 /**
9472  * Averages a 2x2 block of pixel components into a single pixel component, for
9473  * producing the next mipmap level. Increments p and q to the next component.
9474  */
9475 void Texture::
9476 filter_2d_unsigned_byte(unsigned char *&p, const unsigned char *&q,
9477  size_t pixel_size, size_t row_size) {
9478  unsigned int result = ((unsigned int)q[0] +
9479  (unsigned int)q[pixel_size] +
9480  (unsigned int)q[row_size] +
9481  (unsigned int)q[pixel_size + row_size]) >> 2;
9482  *p = (unsigned char)result;
9483  ++p;
9484  ++q;
9485 }
9486 
9487 /**
9488  * Averages a 2x2 block of pixel components into a single pixel component, for
9489  * producing the next mipmap level. Increments p and q to the next component.
9490  */
9491 void Texture::
9492 filter_2d_unsigned_byte_srgb(unsigned char *&p, const unsigned char *&q,
9493  size_t pixel_size, size_t row_size) {
9494  float result = (decode_sRGB_float(q[0]) +
9495  decode_sRGB_float(q[pixel_size]) +
9496  decode_sRGB_float(q[row_size]) +
9497  decode_sRGB_float(q[pixel_size + row_size]));
9498 
9499  *p = encode_sRGB_uchar(result * 0.25f);
9500  ++p;
9501  ++q;
9502 }
9503 
9504 /**
9505  * Averages a 2x2 block of pixel components into a single pixel component, for
9506  * producing the next mipmap level. Increments p and q to the next component.
9507  */
9508 void Texture::
9509 filter_2d_unsigned_byte_srgb_sse2(unsigned char *&p, const unsigned char *&q,
9510  size_t pixel_size, size_t row_size) {
9511  float result = (decode_sRGB_float(q[0]) +
9512  decode_sRGB_float(q[pixel_size]) +
9513  decode_sRGB_float(q[row_size]) +
9514  decode_sRGB_float(q[pixel_size + row_size]));
9515 
9516  *p = encode_sRGB_uchar_sse2(result * 0.25f);
9517  ++p;
9518  ++q;
9519 }
9520 
9521 /**
9522  * Averages a 2x2 block of pixel components into a single pixel component, for
9523  * producing the next mipmap level. Increments p and q to the next component.
9524  */
9525 void Texture::
9526 filter_2d_unsigned_short(unsigned char *&p, const unsigned char *&q,
9527  size_t pixel_size, size_t row_size) {
9528  unsigned int result = ((unsigned int)*(unsigned short *)&q[0] +
9529  (unsigned int)*(unsigned short *)&q[pixel_size] +
9530  (unsigned int)*(unsigned short *)&q[row_size] +
9531  (unsigned int)*(unsigned short *)&q[pixel_size + row_size]) >> 2;
9532  store_unscaled_short(p, result);
9533  q += 2;
9534 }
9535 
9536 /**
9537  * Averages a 2x2 block of pixel components into a single pixel component, for
9538  * producing the next mipmap level. Increments p and q to the next component.
9539  */
9540 void Texture::
9541 filter_2d_float(unsigned char *&p, const unsigned char *&q,
9542  size_t pixel_size, size_t row_size) {
9543  *(float *)p = (*(float *)&q[0] +
9544  *(float *)&q[pixel_size] +
9545  *(float *)&q[row_size] +
9546  *(float *)&q[pixel_size + row_size]) / 4.0f;
9547  p += 4;
9548  q += 4;
9549 }
9550 
9551 /**
9552  * Averages a 2x2x2 block of pixel components into a single pixel component,
9553  * for producing the next mipmap level. Increments p and q to the next
9554  * component.
9555  */
9556 void Texture::
9557 filter_3d_unsigned_byte(unsigned char *&p, const unsigned char *&q,
9558  size_t pixel_size, size_t row_size, size_t page_size) {
9559  unsigned int result = ((unsigned int)q[0] +
9560  (unsigned int)q[pixel_size] +
9561  (unsigned int)q[row_size] +
9562  (unsigned int)q[pixel_size + row_size] +
9563  (unsigned int)q[page_size] +
9564  (unsigned int)q[pixel_size + page_size] +
9565  (unsigned int)q[row_size + page_size] +
9566  (unsigned int)q[pixel_size + row_size + page_size]) >> 3;
9567  *p = (unsigned char)result;
9568  ++p;
9569  ++q;
9570 }
9571 
9572 /**
9573  * Averages a 2x2x2 block of pixel components into a single pixel component,
9574  * for producing the next mipmap level. Increments p and q to the next
9575  * component.
9576  */
9577 void Texture::
9578 filter_3d_unsigned_byte_srgb(unsigned char *&p, const unsigned char *&q,
9579  size_t pixel_size, size_t row_size, size_t page_size) {
9580  float result = (decode_sRGB_float(q[0]) +
9581  decode_sRGB_float(q[pixel_size]) +
9582  decode_sRGB_float(q[row_size]) +
9583  decode_sRGB_float(q[pixel_size + row_size]) +
9584  decode_sRGB_float(q[page_size]) +
9585  decode_sRGB_float(q[pixel_size + page_size]) +
9586  decode_sRGB_float(q[row_size + page_size]) +
9587  decode_sRGB_float(q[pixel_size + row_size + page_size]));
9588 
9589  *p = encode_sRGB_uchar(result * 0.125f);
9590  ++p;
9591  ++q;
9592 }
9593 
9594 /**
9595  * Averages a 2x2x2 block of pixel components into a single pixel component,
9596  * for producing the next mipmap level. Increments p and q to the next
9597  * component.
9598  */
9599 void Texture::
9600 filter_3d_unsigned_byte_srgb_sse2(unsigned char *&p, const unsigned char *&q,
9601  size_t pixel_size, size_t row_size, size_t page_size) {
9602  float result = (decode_sRGB_float(q[0]) +
9603  decode_sRGB_float(q[pixel_size]) +
9604  decode_sRGB_float(q[row_size]) +
9605  decode_sRGB_float(q[pixel_size + row_size]) +
9606  decode_sRGB_float(q[page_size]) +
9607  decode_sRGB_float(q[pixel_size + page_size]) +
9608  decode_sRGB_float(q[row_size + page_size]) +
9609  decode_sRGB_float(q[pixel_size + row_size + page_size]));
9610 
9611  *p = encode_sRGB_uchar_sse2(result * 0.125f);
9612  ++p;
9613  ++q;
9614 }
9615 
9616 /**
9617  * Averages a 2x2x2 block of pixel components into a single pixel component,
9618  * for producing the next mipmap level. Increments p and q to the next
9619  * component.
9620  */
9621 void Texture::
9622 filter_3d_unsigned_short(unsigned char *&p, const unsigned char *&q,
9623  size_t pixel_size, size_t row_size,
9624  size_t page_size) {
9625  unsigned int result = ((unsigned int)*(unsigned short *)&q[0] +
9626  (unsigned int)*(unsigned short *)&q[pixel_size] +
9627  (unsigned int)*(unsigned short *)&q[row_size] +
9628  (unsigned int)*(unsigned short *)&q[pixel_size + row_size] +
9629  (unsigned int)*(unsigned short *)&q[page_size] +
9630  (unsigned int)*(unsigned short *)&q[pixel_size + page_size] +
9631  (unsigned int)*(unsigned short *)&q[row_size + page_size] +
9632  (unsigned int)*(unsigned short *)&q[pixel_size + row_size + page_size]) >> 3;
9633  store_unscaled_short(p, result);
9634  q += 2;
9635 }
9636 
9637 /**
9638  * Averages a 2x2x2 block of pixel components into a single pixel component,
9639  * for producing the next mipmap level. Increments p and q to the next
9640  * component.
9641  */
9642 void Texture::
9643 filter_3d_float(unsigned char *&p, const unsigned char *&q,
9644  size_t pixel_size, size_t row_size, size_t page_size) {
9645  *(float *)p = (*(float *)&q[0] +
9646  *(float *)&q[pixel_size] +
9647  *(float *)&q[row_size] +
9648  *(float *)&q[pixel_size + row_size] +
9649  *(float *)&q[page_size] +
9650  *(float *)&q[pixel_size + page_size] +
9651  *(float *)&q[row_size + page_size] +
9652  *(float *)&q[pixel_size + row_size + page_size]) / 8.0f;
9653  p += 4;
9654  q += 4;
9655 }
9656 
9657 /**
9658  * Invokes the squish library to compress the RAM image(s).
9659  */
9660 bool Texture::
9661 do_squish(CData *cdata, Texture::CompressionMode compression, int squish_flags) {
9662 #ifdef HAVE_SQUISH
9663  if (!do_has_all_ram_mipmap_images(cdata)) {
9664  // If we're about to compress the RAM image, we should ensure that we have
9665  // all of the mipmap levels first.
9666  do_generate_ram_mipmap_images(cdata, false);
9667  }
9668 
9669  RamImages compressed_ram_images;
9670  compressed_ram_images.reserve(cdata->_ram_images.size());
9671  for (size_t n = 0; n < cdata->_ram_images.size(); ++n) {
9672  RamImage compressed_image;
9673  int x_size = do_get_expected_mipmap_x_size(cdata, n);
9674  int y_size = do_get_expected_mipmap_y_size(cdata, n);
9675  int num_pages = do_get_expected_mipmap_num_pages(cdata, n);
9676  int page_size = squish::GetStorageRequirements(x_size, y_size, squish_flags);
9677  int cell_size = squish::GetStorageRequirements(4, 4, squish_flags);
9678 
9679  compressed_image._page_size = page_size;
9680  compressed_image._image = PTA_uchar::empty_array(page_size * num_pages);
9681  for (int z = 0; z < num_pages; ++z) {
9682  unsigned char *dest_page = compressed_image._image.p() + z * page_size;
9683  unsigned const char *source_page = cdata->_ram_images[n]._image.p() + z * cdata->_ram_images[n]._page_size;
9684  unsigned const char *source_page_end = source_page + cdata->_ram_images[n]._page_size;
9685  // Convert one 4 x 4 cell at a time.
9686  unsigned char *d = dest_page;
9687  for (int y = 0; y < y_size; y += 4) {
9688  for (int x = 0; x < x_size; x += 4) {
9689  unsigned char tb[16 * 4];
9690  int mask = 0;
9691  unsigned char *t = tb;
9692  for (int i = 0; i < 16; ++i) {
9693  int xi = x + i % 4;
9694  int yi = y + i / 4;
9695  unsigned const char *s = source_page + (yi * x_size + xi) * cdata->_num_components;
9696  if (s < source_page_end) {
9697  switch (cdata->_num_components) {
9698  case 1:
9699  t[0] = s[0]; // r
9700  t[1] = s[0]; // g
9701  t[2] = s[0]; // b
9702  t[3] = 255; // a
9703  break;
9704 
9705  case 2:
9706  t[0] = s[0]; // r
9707  t[1] = s[0]; // g
9708  t[2] = s[0]; // b
9709  t[3] = s[1]; // a
9710  break;
9711 
9712  case 3:
9713  t[0] = s[2]; // r
9714  t[1] = s[1]; // g
9715  t[2] = s[0]; // b
9716  t[3] = 255; // a
9717  break;
9718 
9719  case 4:
9720  t[0] = s[2]; // r
9721  t[1] = s[1]; // g
9722  t[2] = s[0]; // b
9723  t[3] = s[3]; // a
9724  break;
9725  }
9726  mask |= (1 << i);
9727  }
9728  t += 4;
9729  }
9730  squish::CompressMasked(tb, mask, d, squish_flags);
9731  d += cell_size;
9733  }
9734  }
9735  }
9736  compressed_ram_images.push_back(compressed_image);
9737  }
9738  cdata->_ram_images.swap(compressed_ram_images);
9739  cdata->_ram_image_compression = compression;
9740  return true;
9741 
9742 #else // HAVE_SQUISH
9743  return false;
9744 
9745 #endif // HAVE_SQUISH
9746 }
9747 
9748 /**
9749  * Invokes the squish library to uncompress the RAM image(s).
9750  */
9751 bool Texture::
9752 do_unsquish(CData *cdata, int squish_flags) {
9753 #ifdef HAVE_SQUISH
9754  RamImages uncompressed_ram_images;
9755  uncompressed_ram_images.reserve(cdata->_ram_images.size());
9756  for (size_t n = 0; n < cdata->_ram_images.size(); ++n) {
9757  RamImage uncompressed_image;
9758  int x_size = do_get_expected_mipmap_x_size(cdata, n);
9759  int y_size = do_get_expected_mipmap_y_size(cdata, n);
9760  int num_pages = do_get_expected_mipmap_num_pages(cdata, n);
9761  int page_size = squish::GetStorageRequirements(x_size, y_size, squish_flags);
9762  int cell_size = squish::GetStorageRequirements(4, 4, squish_flags);
9763 
9764  uncompressed_image._page_size = do_get_expected_ram_mipmap_page_size(cdata, n);
9765  uncompressed_image._image = PTA_uchar::empty_array(uncompressed_image._page_size * num_pages);
9766  for (int z = 0; z < num_pages; ++z) {
9767  unsigned char *dest_page = uncompressed_image._image.p() + z * uncompressed_image._page_size;
9768  unsigned char *dest_page_end = dest_page + uncompressed_image._page_size;
9769  unsigned const char *source_page = cdata->_ram_images[n]._image.p() + z * page_size;
9770  // Unconvert one 4 x 4 cell at a time.
9771  unsigned const char *s = source_page;
9772  for (int y = 0; y < y_size; y += 4) {
9773  for (int x = 0; x < x_size; x += 4) {
9774  unsigned char tb[16 * 4];
9775  squish::Decompress(tb, s, squish_flags);
9776  s += cell_size;
9777 
9778  unsigned char *t = tb;
9779  for (int i = 0; i < 16; ++i) {
9780  int xi = x + i % 4;
9781  int yi = y + i / 4;
9782  unsigned char *d = dest_page + (yi * x_size + xi) * cdata->_num_components;
9783  if (d < dest_page_end) {
9784  switch (cdata->_num_components) {
9785  case 1:
9786  d[0] = t[1]; // g
9787  break;
9788 
9789  case 2:
9790  d[0] = t[1]; // g
9791  d[1] = t[3]; // a
9792  break;
9793 
9794  case 3:
9795  d[2] = t[0]; // r
9796  d[1] = t[1]; // g
9797  d[0] = t[2]; // b
9798  break;
9799 
9800  case 4:
9801  d[2] = t[0]; // r
9802  d[1] = t[1]; // g
9803  d[0] = t[2]; // b
9804  d[3] = t[3]; // a
9805  break;
9806  }
9807  }
9808  t += 4;
9809  }
9810  }
9812  }
9813  }
9814  uncompressed_ram_images.push_back(uncompressed_image);
9815  }
9816  cdata->_ram_images.swap(uncompressed_ram_images);
9817  cdata->_ram_image_compression = CM_off;
9818  return true;
9819 
9820 #else // HAVE_SQUISH
9821  return false;
9822 
9823 #endif // HAVE_SQUISH
9824 }
9825 
9826 /**
9827  * Factory method to generate a Texture object
9828  */
9829 void Texture::
9831  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
9832 }
9833 
9834 /**
9835  * Function to write the important information in the particular object to a
9836  * Datagram
9837  */
9838 void Texture::
9840  CDWriter cdata(_cycler, false);
9841 
9842  bool has_rawdata = false;
9843  do_write_datagram_header(cdata, manager, me, has_rawdata);
9844  do_write_datagram_body(cdata, manager, me);
9845 
9846  // If we are also including the texture's image data, then stuff it in here.
9847  if (has_rawdata) {
9848  do_write_datagram_rawdata(cdata, manager, me);
9849  }
9850 }
9851 
9852 /**
9853  * Called by the BamReader to perform any final actions needed for setting up
9854  * the object after all objects have been read and all pointers have been
9855  * completed.
9856  */
9857 void Texture::
9859  // Unref the pointer that we explicitly reffed in make_from_bam().
9860  unref();
9861 
9862  // We should never get back to zero after unreffing our own count, because
9863  // we expect to have been stored in a pointer somewhere. If we do get to
9864  // zero, it's a memory leak; the way to avoid this is to call unref_delete()
9865  // above instead of unref(), but this is dangerous to do from within a
9866  // virtual function.
9867  nassertv(get_ref_count() != 0);
9868 }
9869 
9870 
9871 /**
9872  * Writes the header part of the texture to the Datagram. This is the common
9873  * part that is shared by all Texture subclasses, and contains the filename
9874  * and rawdata flags. This method is not virtual because all Texture
9875  * subclasses must write the same data at this step.
9876  *
9877  * This part must be read first before calling do_fillin_body() to determine
9878  * whether to load the Texture from the TexturePool or directly from the bam
9879  * stream.
9880  *
9881  * After this call, has_rawdata will be filled with either true or false,
9882  * according to whether we expect to write the texture rawdata to the bam
9883  * stream following the texture body.
9884  */
9885 void Texture::
9886 do_write_datagram_header(CData *cdata, BamWriter *manager, Datagram &me, bool &has_rawdata) {
9887  // Write out the texture's raw pixel data if (a) the current Bam Texture
9888  // Mode requires that, or (b) there's no filename, so the file can't be
9889  // loaded up from disk, but the raw pixel data is currently available in
9890  // RAM.
9891 
9892  // Otherwise, we just write out the filename, and assume whoever loads the
9893  // bam file later will have access to the image file on disk.
9894  BamWriter::BamTextureMode file_texture_mode = manager->get_file_texture_mode();
9895  has_rawdata = (file_texture_mode == BamWriter::BTM_rawdata ||
9896  (cdata->_filename.empty() && do_has_bam_rawdata(cdata)));
9897  if (has_rawdata && !do_has_bam_rawdata(cdata)) {
9898  do_get_bam_rawdata(cdata);
9899  if (!do_has_bam_rawdata(cdata)) {
9900  // No image data after all.
9901  has_rawdata = false;
9902  }
9903  }
9904 
9905  bool has_bam_dir = !manager->get_filename().empty();
9906  Filename bam_dir = manager->get_filename().get_dirname();
9907  Filename filename = cdata->_filename;
9908  Filename alpha_filename = cdata->_alpha_filename;
9909 
9911 
9912  switch (file_texture_mode) {
9913  case BamWriter::BTM_unchanged:
9914  case BamWriter::BTM_rawdata:
9915  break;
9916 
9917  case BamWriter::BTM_fullpath:
9918  filename = cdata->_fullpath;
9919  alpha_filename = cdata->_alpha_fullpath;
9920  break;
9921 
9922  case BamWriter::BTM_relative:
9923  filename = cdata->_fullpath;
9924  alpha_filename = cdata->_alpha_fullpath;
9925  bam_dir.make_absolute(vfs->get_cwd());
9926  if (!has_bam_dir || !filename.make_relative_to(bam_dir, true)) {
9927  filename.find_on_searchpath(get_model_path());
9928  }
9929  if (gobj_cat.is_debug()) {
9930  gobj_cat.debug()
9931  << "Texture file " << cdata->_fullpath
9932  << " found as " << filename << "\n";
9933  }
9934  if (!has_bam_dir || !alpha_filename.make_relative_to(bam_dir, true)) {
9935  alpha_filename.find_on_searchpath(get_model_path());
9936  }
9937  if (gobj_cat.is_debug()) {
9938  gobj_cat.debug()
9939  << "Alpha image " << cdata->_alpha_fullpath
9940  << " found as " << alpha_filename << "\n";
9941  }
9942  break;
9943 
9944  case BamWriter::BTM_basename:
9945  filename = cdata->_fullpath.get_basename();
9946  alpha_filename = cdata->_alpha_fullpath.get_basename();
9947  break;
9948 
9949  default:
9950  gobj_cat.error()
9951  << "Unsupported bam-texture-mode: " << (int)file_texture_mode << "\n";
9952  }
9953 
9954  if (filename.empty() && do_has_bam_rawdata(cdata)) {
9955  // If we don't have a filename, we have to store rawdata anyway.
9956  has_rawdata = true;
9957  }
9958 
9959  me.add_string(get_name());
9960  me.add_string(filename);
9961  me.add_string(alpha_filename);
9962  me.add_uint8(cdata->_primary_file_num_channels);
9963  me.add_uint8(cdata->_alpha_file_channel);
9964  me.add_bool(has_rawdata);
9965 
9966  if (manager->get_file_minor_ver() < 25 &&
9967  cdata->_texture_type == TT_cube_map) {
9968  // Between Panda3D releases 1.7.2 and 1.8.0 (bam versions 6.24 and 6.25),
9969  // we added TT_2d_texture_array, shifting the definition for TT_cube_map.
9970  me.add_uint8(TT_2d_texture_array);
9971  } else {
9972  me.add_uint8(cdata->_texture_type);
9973  }
9974 
9975  if (manager->get_file_minor_ver() >= 32) {
9976  me.add_bool(cdata->_has_read_mipmaps);
9977  }
9978 }
9979 
9980 /**
9981  * Writes the body part of the texture to the Datagram. This is generally all
9982  * of the texture parameters except for the header and the rawdata.
9983  */
9984 void Texture::
9985 do_write_datagram_body(CData *cdata, BamWriter *manager, Datagram &me) {
9986  if (manager->get_file_minor_ver() >= 36) {
9987  cdata->_default_sampler.write_datagram(me);
9988  } else {
9989  const SamplerState &s = cdata->_default_sampler;
9990  me.add_uint8(s.get_wrap_u());
9991  me.add_uint8(s.get_wrap_v());
9992  me.add_uint8(s.get_wrap_w());
9993  me.add_uint8(s.get_minfilter());
9994  me.add_uint8(s.get_magfilter());
9996  s.get_border_color().write_datagram(me);
9997  }
9998 
9999  me.add_uint8(cdata->_compression);
10000  me.add_uint8(cdata->_quality_level);
10001 
10002  me.add_uint8(cdata->_format);
10003  me.add_uint8(cdata->_num_components);
10004 
10005  if (cdata->_texture_type == TT_buffer_texture) {
10006  me.add_uint8(cdata->_usage_hint);
10007  }
10008 
10009  if (manager->get_file_minor_ver() >= 28) {
10010  me.add_uint8(cdata->_auto_texture_scale);
10011  }
10012  me.add_uint32(cdata->_orig_file_x_size);
10013  me.add_uint32(cdata->_orig_file_y_size);
10014 
10015  bool has_simple_ram_image = !cdata->_simple_ram_image._image.empty();
10017 
10018  // Write out the simple image too, so it will be available later.
10019  if (has_simple_ram_image) {
10020  me.add_uint32(cdata->_simple_x_size);
10021  me.add_uint32(cdata->_simple_y_size);
10022  me.add_int32(cdata->_simple_image_date_generated);
10023  me.add_uint32(cdata->_simple_ram_image._image.size());
10024  me.append_data(cdata->_simple_ram_image._image, cdata->_simple_ram_image._image.size());
10025  }
10026 }
10027 
10028 /**
10029  * Writes the rawdata part of the texture to the Datagram.
10030  */
10031 void Texture::
10032 do_write_datagram_rawdata(CData *cdata, BamWriter *manager, Datagram &me) {
10033  me.add_uint32(cdata->_x_size);
10034  me.add_uint32(cdata->_y_size);
10035  me.add_uint32(cdata->_z_size);
10036 
10037  if (manager->get_file_minor_ver() >= 30) {
10038  me.add_uint32(cdata->_pad_x_size);
10039  me.add_uint32(cdata->_pad_y_size);
10040  me.add_uint32(cdata->_pad_z_size);
10041  }
10042 
10043  if (manager->get_file_minor_ver() >= 26) {
10044  me.add_uint32(cdata->_num_views);
10045  }
10046  me.add_uint8(cdata->_component_type);
10047  me.add_uint8(cdata->_component_width);
10048  me.add_uint8(cdata->_ram_image_compression);
10049  me.add_uint8(cdata->_ram_images.size());
10050  for (size_t n = 0; n < cdata->_ram_images.size(); ++n) {
10051  me.add_uint32(cdata->_ram_images[n]._page_size);
10052  me.add_uint32(cdata->_ram_images[n]._image.size());
10053  me.append_data(cdata->_ram_images[n]._image, cdata->_ram_images[n]._image.size());
10054  }
10055 }
10056 
10057 /**
10058  * Factory method to generate a Texture object
10059  */
10060 TypedWritable *Texture::
10061 make_from_bam(const FactoryParams &params) {
10062  PT(Texture) dummy = new Texture;
10063  return dummy->make_this_from_bam(params);
10064 }
10065 
10066 /**
10067  * Called by make_from_bam() once the particular subclass of Texture is known.
10068  * This is called on a newly-constructed Texture object of the appropriate
10069  * subclass. It will return either the same Texture object (e.g. this), or a
10070  * different Texture object loaded via the TexturePool, as appropriate.
10071  */
10072 TypedWritable *Texture::
10073 make_this_from_bam(const FactoryParams &params) {
10074  // The process of making a texture is slightly different than making other
10075  // TypedWritable objects. That is because all creation of Textures should
10076  // be done through calls to TexturePool, which ensures that any loads of the
10077  // same filename refer to the same memory.
10078 
10079  DatagramIterator scan;
10080  BamReader *manager;
10081 
10082  parse_params(params, scan, manager);
10083 
10084  // Get the header information--the filenames and texture type--so we can
10085  // look up the file on disk first.
10086  string name = scan.get_string();
10087  Filename filename = scan.get_string();
10088  Filename alpha_filename = scan.get_string();
10089 
10090  int primary_file_num_channels = scan.get_uint8();
10091  int alpha_file_channel = scan.get_uint8();
10092  bool has_rawdata = scan.get_bool();
10093  TextureType texture_type = (TextureType)scan.get_uint8();
10094  if (manager->get_file_minor_ver() < 25) {
10095  // Between Panda3D releases 1.7.2 and 1.8.0 (bam versions 6.24 and 6.25),
10096  // we added TT_2d_texture_array, shifting the definition for TT_cube_map.
10097  if (texture_type == TT_2d_texture_array) {
10098  texture_type = TT_cube_map;
10099  }
10100  }
10101  bool has_read_mipmaps = false;
10102  if (manager->get_file_minor_ver() >= 32) {
10103  has_read_mipmaps = scan.get_bool();
10104  }
10105 
10106  Texture *me = nullptr;
10107  if (has_rawdata) {
10108  // If the raw image data is included, then just load the texture directly
10109  // from the stream, and return it. In this case we return the "this"
10110  // pointer, since it's a newly-created Texture object of the appropriate
10111  // type.
10112  me = this;
10113  me->set_name(name);
10114  CDWriter cdata_me(me->_cycler, true);
10115  cdata_me->_filename = filename;
10116  cdata_me->_alpha_filename = alpha_filename;
10117  cdata_me->_primary_file_num_channels = primary_file_num_channels;
10118  cdata_me->_alpha_file_channel = alpha_file_channel;
10119  cdata_me->_texture_type = texture_type;
10120  cdata_me->_has_read_mipmaps = has_read_mipmaps;
10121 
10122  // Read the texture attributes directly from the bam stream.
10123  me->do_fillin_body(cdata_me, scan, manager);
10124  me->do_fillin_rawdata(cdata_me, scan, manager);
10125 
10126  // To manage the reference count, explicitly ref it now, then unref it in
10127  // the finalize callback.
10128  me->ref();
10129  manager->register_finalize(me);
10130 
10131  } else {
10132  // The raw image data isn't included, so we'll be loading the Texture via
10133  // the TexturePool. In this case we use the "this" pointer as a temporary
10134  // object to read all of the attributes from the bam stream.
10135  Texture *dummy = this;
10136  AutoTextureScale auto_texture_scale = ATS_unspecified;
10137  {
10138  CDWriter cdata_dummy(dummy->_cycler, true);
10139  dummy->do_fillin_body(cdata_dummy, scan, manager);
10140  auto_texture_scale = cdata_dummy->_auto_texture_scale;
10141  }
10142 
10143  if (filename.empty()) {
10144  // This texture has no filename; since we don't have an image to load,
10145  // we can't actually create the texture.
10146  gobj_cat.info()
10147  << "Cannot create texture '" << name << "' with no filename.\n";
10148 
10149  } else {
10150  // This texture does have a filename, so try to load it from disk.
10152  if (!manager->get_filename().empty()) {
10153  // If texture filename was given relative to the bam filename, expand
10154  // it now.
10155  Filename bam_dir = manager->get_filename().get_dirname();
10156  vfs->resolve_filename(filename, bam_dir);
10157  if (!alpha_filename.empty()) {
10158  vfs->resolve_filename(alpha_filename, bam_dir);
10159  }
10160  }
10161 
10162  LoaderOptions options = manager->get_loader_options();
10163  if (dummy->uses_mipmaps()) {
10164  options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_generate_mipmaps);
10165  }
10166  options.set_auto_texture_scale(auto_texture_scale);
10167 
10168  switch (texture_type) {
10169  case TT_buffer_texture:
10170  case TT_1d_texture:
10171  case TT_2d_texture:
10172  case TT_1d_texture_array:
10173  if (alpha_filename.empty()) {
10174  me = TexturePool::load_texture(filename, primary_file_num_channels,
10175  has_read_mipmaps, options);
10176  } else {
10177  me = TexturePool::load_texture(filename, alpha_filename,
10178  primary_file_num_channels,
10179  alpha_file_channel,
10180  has_read_mipmaps, options);
10181  }
10182  break;
10183 
10184  case TT_3d_texture:
10185  me = TexturePool::load_3d_texture(filename, has_read_mipmaps, options);
10186  break;
10187 
10188  case TT_2d_texture_array:
10189  case TT_cube_map_array:
10190  me = TexturePool::load_2d_texture_array(filename, has_read_mipmaps, options);
10191  break;
10192 
10193  case TT_cube_map:
10194  me = TexturePool::load_cube_map(filename, has_read_mipmaps, options);
10195  break;
10196  }
10197  }
10198 
10199  if (me != nullptr) {
10200  me->set_name(name);
10201  CDWriter cdata_me(me->_cycler, true);
10202  me->do_fillin_from(cdata_me, dummy);
10203 
10204  // Since in this case me was loaded from the TexturePool, there's no
10205  // need to explicitly manage the reference count. TexturePool will hold
10206  // it safely.
10207  }
10208  }
10209 
10210  return me;
10211 }
10212 
10213 /**
10214  * Reads in the part of the Texture that was written with
10215  * do_write_datagram_body().
10216  */
10217 void Texture::
10218 do_fillin_body(CData *cdata, DatagramIterator &scan, BamReader *manager) {
10219  cdata->_default_sampler.read_datagram(scan, manager);
10220 
10221  if (manager->get_file_minor_ver() >= 1) {
10222  cdata->_compression = (CompressionMode)scan.get_uint8();
10223  }
10224  if (manager->get_file_minor_ver() >= 16) {
10225  cdata->_quality_level = (QualityLevel)scan.get_uint8();
10226  }
10227 
10228  cdata->_format = (Format)scan.get_uint8();
10229  cdata->_num_components = scan.get_uint8();
10230 
10231  if (cdata->_texture_type == TT_buffer_texture) {
10232  cdata->_usage_hint = (GeomEnums::UsageHint)scan.get_uint8();
10233  }
10234 
10235  cdata->inc_properties_modified();
10236 
10237  cdata->_auto_texture_scale = ATS_unspecified;
10238  if (manager->get_file_minor_ver() >= 28) {
10239  cdata->_auto_texture_scale = (AutoTextureScale)scan.get_uint8();
10240  }
10241 
10242  bool has_simple_ram_image = false;
10243  if (manager->get_file_minor_ver() >= 18) {
10244  cdata->_orig_file_x_size = scan.get_uint32();
10245  cdata->_orig_file_y_size = scan.get_uint32();
10246 
10247  has_simple_ram_image = scan.get_bool();
10248  }
10249 
10250  if (has_simple_ram_image) {
10251  cdata->_simple_x_size = scan.get_uint32();
10252  cdata->_simple_y_size = scan.get_uint32();
10253  cdata->_simple_image_date_generated = scan.get_int32();
10254 
10255  size_t u_size = scan.get_uint32();
10256 
10257  // Protect against large allocation.
10258  if (u_size > scan.get_remaining_size()) {
10259  gobj_cat.error()
10260  << "simple RAM image extends past end of datagram, is texture corrupt?\n";
10261  return;
10262  }
10263 
10264  PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type());
10265  scan.extract_bytes(image.p(), u_size);
10266 
10267  cdata->_simple_ram_image._image = image;
10268  cdata->_simple_ram_image._page_size = u_size;
10269  cdata->inc_simple_image_modified();
10270  }
10271 }
10272 
10273 /**
10274  * Reads in the part of the Texture that was written with
10275  * do_write_datagram_rawdata().
10276  */
10277 void Texture::
10278 do_fillin_rawdata(CData *cdata, DatagramIterator &scan, BamReader *manager) {
10279  cdata->_x_size = scan.get_uint32();
10280  cdata->_y_size = scan.get_uint32();
10281  cdata->_z_size = scan.get_uint32();
10282 
10283  if (manager->get_file_minor_ver() >= 30) {
10284  cdata->_pad_x_size = scan.get_uint32();
10285  cdata->_pad_y_size = scan.get_uint32();
10286  cdata->_pad_z_size = scan.get_uint32();
10287  } else {
10288  do_set_pad_size(cdata, 0, 0, 0);
10289  }
10290 
10291  cdata->_num_views = 1;
10292  if (manager->get_file_minor_ver() >= 26) {
10293  cdata->_num_views = scan.get_uint32();
10294  }
10295  cdata->_component_type = (ComponentType)scan.get_uint8();
10296  cdata->_component_width = scan.get_uint8();
10297  cdata->_ram_image_compression = CM_off;
10298  if (manager->get_file_minor_ver() >= 1) {
10299  cdata->_ram_image_compression = (CompressionMode)scan.get_uint8();
10300  }
10301 
10302  int num_ram_images = 1;
10303  if (manager->get_file_minor_ver() >= 3) {
10304  num_ram_images = scan.get_uint8();
10305  }
10306 
10307  cdata->_ram_images.clear();
10308  cdata->_ram_images.reserve(num_ram_images);
10309  for (int n = 0; n < num_ram_images; ++n) {
10310  cdata->_ram_images.push_back(RamImage());
10311  cdata->_ram_images[n]._page_size = get_expected_ram_page_size();
10312  if (manager->get_file_minor_ver() >= 1) {
10313  cdata->_ram_images[n]._page_size = scan.get_uint32();
10314  }
10315 
10316  // fill the cdata->_image buffer with image data
10317  size_t u_size = scan.get_uint32();
10318 
10319  // Protect against large allocation.
10320  if (u_size > scan.get_remaining_size()) {
10321  gobj_cat.error()
10322  << "RAM image " << n << " extends past end of datagram, is texture corrupt?\n";
10323  return;
10324  }
10325 
10326  PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type());
10327  scan.extract_bytes(image.p(), u_size);
10328 
10329  cdata->_ram_images[n]._image = image;
10330  }
10331  cdata->_loaded_from_image = true;
10332  cdata->inc_image_modified();
10333 }
10334 
10335 /**
10336  * Called in make_from_bam(), this method properly copies the attributes from
10337  * the bam stream (as stored in dummy) into this texture, updating the
10338  * modified flags appropriately.
10339  */
10340 void Texture::
10341 do_fillin_from(CData *cdata, const Texture *dummy) {
10342  // Use the setters instead of setting these directly, so we can correctly
10343  // avoid incrementing cdata->_properties_modified if none of these actually
10344  // change. (Otherwise, we'd have to reload the texture to the GSG every
10345  // time we loaded a new bam file that reference the texture, since each bam
10346  // file reference passes through this function.)
10347 
10348  CDReader cdata_dummy(dummy->_cycler);
10349 
10350  do_set_wrap_u(cdata, cdata_dummy->_default_sampler.get_wrap_u());
10351  do_set_wrap_v(cdata, cdata_dummy->_default_sampler.get_wrap_v());
10352  do_set_wrap_w(cdata, cdata_dummy->_default_sampler.get_wrap_w());
10353  do_set_border_color(cdata, cdata_dummy->_default_sampler.get_border_color());
10354 
10355  if (cdata_dummy->_default_sampler.get_minfilter() != SamplerState::FT_default) {
10356  do_set_minfilter(cdata, cdata_dummy->_default_sampler.get_minfilter());
10357  }
10358  if (cdata_dummy->_default_sampler.get_magfilter() != SamplerState::FT_default) {
10359  do_set_magfilter(cdata, cdata_dummy->_default_sampler.get_magfilter());
10360  }
10361  if (cdata_dummy->_default_sampler.get_anisotropic_degree() != 0) {
10362  do_set_anisotropic_degree(cdata, cdata_dummy->_default_sampler.get_anisotropic_degree());
10363  }
10364  if (cdata_dummy->_compression != CM_default) {
10365  do_set_compression(cdata, cdata_dummy->_compression);
10366  }
10367  if (cdata_dummy->_quality_level != QL_default) {
10368  do_set_quality_level(cdata, cdata_dummy->_quality_level);
10369  }
10370 
10371  Format format = cdata_dummy->_format;
10372  int num_components = cdata_dummy->_num_components;
10373 
10374  if (num_components == cdata->_num_components) {
10375  // Only reset the format if the number of components hasn't changed, since
10376  // if the number of components has changed our texture no longer matches
10377  // what it was when the bam was written.
10378  do_set_format(cdata, format);
10379  }
10380 
10381  if (!cdata_dummy->_simple_ram_image._image.empty()) {
10382  // Only replace the simple ram image if it was generated more recently
10383  // than the one we already have.
10384  if (cdata->_simple_ram_image._image.empty() ||
10385  cdata_dummy->_simple_image_date_generated > cdata->_simple_image_date_generated) {
10386  do_set_simple_ram_image(cdata,
10387  cdata_dummy->_simple_ram_image._image,
10388  cdata_dummy->_simple_x_size,
10389  cdata_dummy->_simple_y_size);
10390  cdata->_simple_image_date_generated = cdata_dummy->_simple_image_date_generated;
10391  }
10392  }
10393 }
10394 
10395 /**
10396  *
10397  */
10398 Texture::CData::
10399 CData() {
10400  _primary_file_num_channels = 0;
10401  _alpha_file_channel = 0;
10402  _keep_ram_image = true;
10403  _compression = CM_default;
10404  _auto_texture_scale = ATS_unspecified;
10405  _ram_image_compression = CM_off;
10406  _render_to_texture = false;
10407  _match_framebuffer_format = false;
10408  _post_load_store_cache = false;
10409  _quality_level = QL_default;
10410 
10411  _texture_type = TT_2d_texture;
10412  _x_size = 0;
10413  _y_size = 1;
10414  _z_size = 1;
10415  _num_views = 1;
10416 
10417  // We will override the format in a moment (in the Texture constructor), but
10418  // set it to something else first to avoid the check in do_set_format
10419  // depending on an uninitialized value.
10420  _format = F_rgba;
10421 
10422  // Only used for buffer textures.
10423  _usage_hint = GeomEnums::UH_unspecified;
10424 
10425  _pad_x_size = 0;
10426  _pad_y_size = 0;
10427  _pad_z_size = 0;
10428 
10429  _orig_file_x_size = 0;
10430  _orig_file_y_size = 0;
10431 
10432  _loaded_from_image = false;
10433  _loaded_from_txo = false;
10434  _has_read_pages = false;
10435  _has_read_mipmaps = false;
10436  _num_mipmap_levels_read = 0;
10437 
10438  _simple_x_size = 0;
10439  _simple_y_size = 0;
10440  _simple_ram_image._page_size = 0;
10441 
10442  _has_clear_color = false;
10443 }
10444 
10445 /**
10446  *
10447  */
10448 Texture::CData::
10449 CData(const Texture::CData &copy) {
10450  _num_mipmap_levels_read = 0;
10451 
10452  do_assign(&copy);
10453 
10454  _properties_modified = copy._properties_modified;
10455  _image_modified = copy._image_modified;
10456  _simple_image_modified = copy._simple_image_modified;
10457 }
10458 
10459 /**
10460  *
10461  */
10462 CycleData *Texture::CData::
10463 make_copy() const {
10464  return new CData(*this);
10465 }
10466 
10467 /**
10468  *
10469  */
10470 void Texture::CData::
10471 do_assign(const Texture::CData *copy) {
10472  _filename = copy->_filename;
10473  _alpha_filename = copy->_alpha_filename;
10474  if (!copy->_fullpath.empty()) {
10475  // Since the fullpath is often empty on a file loaded directly from a txo,
10476  // we only assign the fullpath if it is not empty.
10477  _fullpath = copy->_fullpath;
10478  _alpha_fullpath = copy->_alpha_fullpath;
10479  }
10480  _primary_file_num_channels = copy->_primary_file_num_channels;
10481  _alpha_file_channel = copy->_alpha_file_channel;
10482  _x_size = copy->_x_size;
10483  _y_size = copy->_y_size;
10484  _z_size = copy->_z_size;
10485  _num_views = copy->_num_views;
10486  _pad_x_size = copy->_pad_x_size;
10487  _pad_y_size = copy->_pad_y_size;
10488  _pad_z_size = copy->_pad_z_size;
10489  _orig_file_x_size = copy->_orig_file_x_size;
10490  _orig_file_y_size = copy->_orig_file_y_size;
10491  _num_components = copy->_num_components;
10492  _component_width = copy->_component_width;
10493  _texture_type = copy->_texture_type;
10494  _format = copy->_format;
10495  _component_type = copy->_component_type;
10496  _loaded_from_image = copy->_loaded_from_image;
10497  _loaded_from_txo = copy->_loaded_from_txo;
10498  _has_read_pages = copy->_has_read_pages;
10499  _has_read_mipmaps = copy->_has_read_mipmaps;
10500  _num_mipmap_levels_read = copy->_num_mipmap_levels_read;
10501  _default_sampler = copy->_default_sampler;
10502  _keep_ram_image = copy->_keep_ram_image;
10503  _compression = copy->_compression;
10504  _match_framebuffer_format = copy->_match_framebuffer_format;
10505  _quality_level = copy->_quality_level;
10506  _auto_texture_scale = copy->_auto_texture_scale;
10507  _ram_image_compression = copy->_ram_image_compression;
10508  _ram_images = copy->_ram_images;
10509  _simple_x_size = copy->_simple_x_size;
10510  _simple_y_size = copy->_simple_y_size;
10511  _simple_ram_image = copy->_simple_ram_image;
10512 }
10513 
10514 /**
10515  * Writes the contents of this object to the datagram for shipping out to a
10516  * Bam file.
10517  */
10518 void Texture::CData::
10519 write_datagram(BamWriter *manager, Datagram &dg) const {
10520 }
10521 
10522 /**
10523  * Receives an array of pointers, one for each time manager->read_pointer()
10524  * was called in fillin(). Returns the number of pointers processed.
10525  */
10526 int Texture::CData::
10527 complete_pointers(TypedWritable **p_list, BamReader *manager) {
10528  return 0;
10529 }
10530 
10531 /**
10532  * This internal function is called by make_from_bam to read in all of the
10533  * relevant data from the BamFile for the new Geom.
10534  */
10535 void Texture::CData::
10536 fillin(DatagramIterator &scan, BamReader *manager) {
10537 }
10538 
10539 /**
10540  *
10541  */
10542 ostream &
10543 operator << (ostream &out, Texture::TextureType tt) {
10544  return out << Texture::format_texture_type(tt);
10545 }
10546 
10547 /**
10548  *
10549  */
10550 ostream &
10551 operator << (ostream &out, Texture::ComponentType ct) {
10552  return out << Texture::format_component_type(ct);
10553 }
10554 
10555 /**
10556  *
10557  */
10558 ostream &
10559 operator << (ostream &out, Texture::Format f) {
10560  return out << Texture::format_format(f);
10561 }
10562 
10563 /**
10564  *
10565  */
10566 ostream &
10567 operator << (ostream &out, Texture::CompressionMode cm) {
10568  return out << Texture::format_compression_mode(cm);
10569 }
10570 
10571 /**
10572  *
10573  */
10574 ostream &
10575 operator << (ostream &out, Texture::QualityLevel tql) {
10576  return out << Texture::format_quality_level(tql);
10577 }
10578 
10579 /**
10580  *
10581  */
10582 istream &
10583 operator >> (istream &in, Texture::QualityLevel &tql) {
10584  string word;
10585  in >> word;
10586 
10587  tql = Texture::string_quality_level(word);
10588  return in;
10589 }
MutexHolder
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:25
SamplerState::get_anisotropic_degree
get_anisotropic_degree
Returns the degree of anisotropic filtering that should be applied to the texture.
Definition: samplerState.h:119
TexturePool::load_2d_texture_array
static Texture * load_2d_texture_array(const Filename &filename_pattern, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads a 2-D texture array that is specified with a series of n pages, all numbered in sequence,...
Definition: texturePool.I:101
bamCache.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DatagramIterator::get_string
std::string get_string()
Extracts a variable-length string.
Definition: datagramIterator.cxx:26
indent
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PreparedGraphicsObjects
A table of objects that are saved within the graphics context for reference by handle later.
Definition: preparedGraphicsObjects.h:58
PNMImage::copy_header_from
void copy_header_from(const PNMImageHeader &header)
Copies just the header information into this image.
Definition: pnmImage.cxx:200
PNMImage::get_red_val
xelval get_red_val(int x, int y) const
Returns the red component color at the indicated pixel.
Definition: pnmImage.I:452
PNMImage::get_color_space
ColorSpace get_color_space() const
Returns the color space in which the image is encoded.
Definition: pnmImage.I:332
texturePool.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT
PT(Texture) Texture
Constructs a new Texture object from the txo file.
Definition: texture.cxx:854
textureContext.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
LoaderOptions::get_auto_texture_scale
get_auto_texture_scale
See set_auto_texture_scale().
Definition: loaderOptions.h:69
TexturePool::load_cube_map
static Texture * load_cube_map(const Filename &filename_pattern, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads a cube map texture that is specified with a series of 6 pages, numbered 0 through 5.
Definition: texturePool.I:118
Texture::new_simple_ram_image
PTA_uchar new_simple_ram_image(int x_size, int y_size)
Creates an empty array for the simple ram image of the indicated size, and returns a modifiable point...
Definition: texture.cxx:1297
BamCache::get_cache_textures
get_cache_textures
Returns whether texture files (e.g.
Definition: bamCache.h:90
PNMImage::get_alpha_array
xelval * get_alpha_array()
Directly access the underlying PNMImage array of alpha values.
Definition: pnmImage.I:1115
CycleData
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:47
config_putil.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DatagramIterator::get_int32
int32_t get_int32()
Extracts a signed 32-bit integer.
Definition: datagramIterator.I:107
PreparedGraphicsObjects::release_texture
void release_texture(TextureContext *tc)
Indicates that a texture context, created by a previous call to prepare_texture(),...
Definition: preparedGraphicsObjects.cxx:270
VirtualFileSystem::get_cwd
Filename get_cwd() const
Returns the current directory name.
Definition: virtualFileSystem.cxx:455
BamReader::get_loader_options
get_loader_options
Returns the LoaderOptions passed to the loader when the model was requested, if any.
Definition: bamReader.h:156
pbitops.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
BamCacheRecord::set_data
set_data
Stores a new data object on the record.
Definition: bamCacheRecord.h:77
BamCacheRecord
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
Definition: bamCacheRecord.h:35
DatagramInputFile::read_header
bool read_header(std::string &header, size_t num_bytes)
Reads a sequence of bytes from the beginning of the datagram file.
Definition: datagramInputFile.cxx:104
PfmFile::clear
void clear()
Eliminates all data in the file.
Definition: pfmFile.cxx:77
pvector
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
PfmFile::get_channel
PN_float32 get_channel(int x, int y, int c) const
Returns the cth channel of the point value at the indicated point.
Definition: pfmFile.I:52
BufferContext::get_data_size_bytes
get_data_size_bytes
Returns the number of bytes previously reported for the data object.
Definition: bufferContext.h:53
Texture::format_format
static std::string format_format(Format f)
Returns the indicated Format converted to a string word.
Definition: texture.cxx:2163
PNMImage::set_num_channels
void set_num_channels(int num_channels)
Changes the number of channels associated with the image.
Definition: pnmImage.I:353
string_utils.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ConfigVariableList::get_unique_value
std::string get_unique_value(size_t n) const
Returns the nth unique value of the variable.
Definition: configVariableList.I:77
config_gobj.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PfmFile::write
bool write(const Filename &fullpath)
Writes the PFM data to the indicated file, returning true on success, false on failure.
Definition: pfmFile.cxx:204
Texture::has_compression
bool has_compression() const
Returns true if the texture indicates it wants to be compressed, either with CM_on or higher,...
Definition: texture.I:1102
Texture::is_srgb
static bool is_srgb(Format format)
Returns true if the indicated format is in the sRGB color space, false otherwise.
Definition: texture.cxx:2617
Texture::set_ram_mipmap_pointer_from_int
void set_ram_mipmap_pointer_from_int(long long pointer, int n, int page_size)
Accepts a raw pointer cast as an int, which is then passed to set_ram_mipmap_pointer(); see the docum...
Definition: texture.cxx:1263
Texture::get_active
bool get_active(PreparedGraphicsObjects *prepared_objects) const
Returns true if this Texture was rendered in the most recent frame within the indicated GSG.
Definition: texture.cxx:1513
PNMImage::clear
void clear()
Frees all memory allocated for the image, and clears all its parameters (size, color,...
Definition: pnmImage.cxx:48
pandabase.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Texture::string_texture_type
static TextureType string_texture_type(const std::string &str)
Returns the TextureType corresponding to the indicated string word.
Definition: texture.cxx:2078
pnmImage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
datagramInputFile.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Texture::get_textures_power_2
static AutoTextureScale get_textures_power_2()
This flag returns ATS_none, ATS_up, or ATS_down and controls the scaling of textures in general.
Definition: texture.I:1863
PipelineCycler::write_upstream
CycleDataType * write_upstream(bool force_to_0, Thread *current_thread)
See PipelineCyclerBase::write_upstream().
Definition: pipelineCycler.I:258
PNMImage::get_alpha_val
xelval get_alpha_val(int x, int y) const
Returns the alpha component color at the indicated pixel.
Definition: pnmImage.I:494
BamCache::get_global_ptr
static BamCache * get_global_ptr()
Returns a pointer to the global BamCache object, which is used automatically by the ModelPool and Tex...
Definition: bamCache.I:223
PNMImage::is_valid
bool is_valid() const
Returns true if the image has been read in or correctly initialized with a height and width.
Definition: pnmImage.I:342
MutexDirect::lock
void lock()
Alias for acquire() to match C++11 semantics.
Definition: mutexDirect.I:19
Datagram::add_uint8
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
DatagramIterator
A class to retrieve the individual data elements previously stored in a Datagram.
Definition: datagramIterator.h:27
pmap
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
VirtualFileSystem::get_file
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
Definition: virtualFileSystem.cxx:516
Texture::up_to_power_2
static int up_to_power_2(int value)
Returns the smallest power of 2 greater than or equal to value.
Definition: texture.cxx:1983
BamReader
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
Texture::release_all
int release_all()
Frees the context allocated on all objects for which the texture has been declared.
Definition: texture.cxx:1593
pnmReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
preparedGraphicsObjects.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Texture::write_datagram
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
Definition: texture.cxx:9839
Thread::consider_yield
static void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
Definition: thread.I:212
Texture::register_with_read_factory
static void register_with_read_factory()
Factory method to generate a Texture object.
Definition: texture.cxx:9830
Texture::texture_uploaded
void texture_uploaded()
This method is called by the GraphicsEngine at the beginning of the frame *after* a texture has been ...
Definition: texture.cxx:2492
decode_sRGB_float
BEGIN_PUBLISH EXPCL_PANDA_PNMIMAGE float decode_sRGB_float(unsigned char val)
Decodes the sRGB-encoded unsigned char value to a linearized float in the range 0-1.
Definition: convert_srgb.I:18
Texture::consider_rescale
void consider_rescale(PNMImage &pnmimage)
Asks the PNMImage to change its scale when it reads the image, according to the whims of the Config....
Definition: texture.cxx:2014
PNMImageHeader::get_maxval
get_maxval
Returns the maximum channel value allowable for any pixel in this image; for instance,...
Definition: pnmImageHeader.h:70
Texture::get_auto_texture_scale
get_auto_texture_scale
Returns the power-of-2 texture-scaling mode that will be applied to this particular texture when it i...
Definition: texture.h:524
Filename::get_basename_wo_extension
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
PNMImage::get_array
xel * get_array()
Directly access the underlying PNMImage array.
Definition: pnmImage.I:1098
Texture::format_quality_level
static std::string format_quality_level(QualityLevel tql)
Returns the indicated QualityLevel converted to a string word.
Definition: texture.cxx:2446
pStatTimer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Texture::is_prepared
bool is_prepared(PreparedGraphicsObjects *prepared_objects) const
Returns true if the texture has already been prepared or enqueued for preparation on the indicated GS...
Definition: texture.cxx:1438
bamCacheRecord.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PNMImage::set_alpha
void set_alpha(int x, int y, float a)
Sets the alpha component color only at the indicated pixel.
Definition: pnmImage.I:859
SamplerState::get_border_color
get_border_color
Returns the solid color of the texture's border.
Definition: samplerState.h:121
PreparedGraphicsObjects::dequeue_texture
bool dequeue_texture(Texture *tex)
Removes a texture from the queued list of textures to be prepared.
Definition: preparedGraphicsObjects.cxx:238
BamWriter
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
get_lowest_on_bit
int get_lowest_on_bit(uint16_t x)
Returns the index of the lowest 1 bit in the word.
Definition: pbitops.I:129
CullTraverser
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
Texture::modify_simple_ram_image
PTA_uchar modify_simple_ram_image()
Returns a modifiable pointer to the internal "simple" texture image.
Definition: texture.cxx:1286
SamplerState::get_magfilter
get_magfilter
Returns the filter mode of the texture for magnification.
Definition: samplerState.h:116
Filename::get_filename_index
Filename get_filename_index(int index) const
If the pattern flag is set for this Filename and the filename string actually includes a sequence of ...
Definition: filename.cxx:836
Texture::is_unsigned
static bool is_unsigned(ComponentType ctype)
Returns true if the indicated component type is unsigned, false otherwise.
Definition: texture.cxx:2545
InternalName
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
bam.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ConstPointerToArray< unsigned char >
DatagramInputFile::open
bool open(const FileReference *file)
Opens the indicated filename for reading.
Definition: datagramInputFile.cxx:33
Texture::read_ktx
bool read_ktx(std::istream &in, const std::string &filename="", bool header_only=false)
Reads the texture from a KTX file object.
Definition: texture.cxx:955
Texture::read_dds
bool read_dds(std::istream &in, const std::string &filename="", bool header_only=false)
Reads the texture from a DDS file object.
Definition: texture.cxx:938
BamReader::get_factory
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
Texture::get_aux_data
get_aux_data
Returns a record previously recorded via set_aux_data().
Definition: texture.h:544
PfmFile::read
bool read(const Filename &fullpath)
Reads the PFM data from the indicated file, returning true on success, false on failure.
Definition: pfmFile.cxx:121
Texture
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
pfmFile.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PipelineCyclerTrivialImpl::release_read
void release_read(const CycleData *pointer) const
Releases a pointer previously obtained via a call to read().
Definition: pipelineCyclerTrivialImpl.I:92
ConfigVariableEnum
This class specializes ConfigVariable as an enumerated type.
Definition: configVariableEnum.h:31
DatagramInputFile
This class can be used to read a binary file that consists of an arbitrary header followed by a numbe...
Definition: datagramInputFile.h:28
GlobPattern::matches
bool matches(const std::string &candidate) const
Returns true if the candidate string matches the pattern, false otherwise.
Definition: globPattern.I:122
GraphicsStateGuardianBase::get_default_gsg
static GraphicsStateGuardianBase * get_default_gsg()
Returns a pointer to the "default" GSG.
Definition: graphicsStateGuardianBase.cxx:31
BamWriter::get_file_texture_mode
get_file_texture_mode
Returns the BamTextureMode preference indicated by the Bam file currently being written.
Definition: bamWriter.h:95
bamReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Texture::write
bool write(const Filename &fullpath)
Writes the texture to the named filename.
Definition: texture.I:298
PNMImage::read
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:278
Texture::is_cacheable
is_cacheable
Returns true if there is enough information in this Texture object to write it to the bam cache succe...
Definition: texture.h:465
VirtualFileSystem::exists
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
Definition: virtualFileSystem.I:18
cmath.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Texture::prepare_now
TextureContext * prepare_now(int view, PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg)
Creates a context for the texture on the particular GSG, if it does not already exist.
Definition: texture.cxx:1956
LoaderOptions
Specifies parameters that may be passed to the loader.
Definition: loaderOptions.h:23
TypedReferenceCount
A base class for things which need to inherit from both TypedObject and from ReferenceCount.
Definition: typedReferenceCount.h:31
TypedWritable
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
PNMImage::get_green_val
xelval get_green_val(int x, int y) const
Returns the green component color at the indicated pixel.
Definition: pnmImage.I:462
PNMImage
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:58
Datagram
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
Texture::has_alpha
static bool has_alpha(Format format)
Returns true if the indicated format includes alpha, false otherwise.
Definition: texture.cxx:2573
get_next_higher_bit
int get_next_higher_bit(uint16_t x)
Returns the smallest power of 2 greater than x.
Definition: pbitops.I:244
PNMImage::quick_filter_from
void quick_filter_from(const PNMImage &copy, int xborder=0, int yborder=0)
Resizes from the given image, with a fixed radius of 0.5.
Definition: pnm-image-filter.cxx:754
PNMImageHeader::get_x_size
int get_x_size() const
Returns the number of pixels in the X direction.
Definition: pnmImageHeader.I:144
PNMImageHeader::get_num_channels
get_num_channels
Returns the number of channels in the image.
Definition: pnmImageHeader.h:60
AsyncFuture
This class represents a thread-safe handle to a promised future result of an asynchronous operation,...
Definition: asyncFuture.h:61
Texture::string_format
static Format string_format(const std::string &str)
Returns the Format corresponding to the indicated string word.
Definition: texture.cxx:2265
PfmFile::load
bool load(const PNMImage &pnmimage)
Fills the PfmFile with the data from the indicated PNMImage, converted to floating-point values.
Definition: pfmFile.cxx:287
Thread::get_current_thread
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
PfmFile
Defines a pfm file, a 2-d table of floating-point numbers, either 3-component or 1-component,...
Definition: pfmFile.h:31
BamWriter::get_file_minor_ver
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being written.
Definition: bamWriter.I:59
PStatTimer
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
Texture::load_related
Texture * load_related(const InternalName *suffix) const
Loads a texture whose filename is derived by concatenating a suffix to the filename of this texture.
Definition: texture.cxx:968
PNMImage::set_red
void set_red(int x, int y, float r)
Sets the red component color only at the indicated pixel.
Definition: pnmImage.I:818
Datagram::add_string
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
BufferContext::get_resident
get_resident
Returns the resident flag associated with this object.
Definition: bufferContext.h:56
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
BamReader::register_finalize
void register_finalize(TypedWritable *whom)
Should be called by an object reading itself from the Bam file to indicate that this particular objec...
Definition: bamReader.cxx:808
BamReader::init
bool init()
Initializes the BamReader prior to reading any objects from its source.
Definition: bamReader.cxx:85
VirtualFileSystem::close_write_file
static void close_write_file(std::ostream *stream)
Closes a file opened by a previous call to open_write_file().
Definition: virtualFileSystem.cxx:930
upcase
string upcase(const string &s)
Returns the input string with all lowercase letters converted to uppercase.
Definition: string_utils.cxx:85
Texture::set_orig_file_size
void set_orig_file_size(int x, int y, int z=1)
Specifies the size of the texture as it exists in its original disk file, before any Panda scaling.
Definition: texture.cxx:1936
SamplerState
Represents a set of settings that indicate how a texture is sampled.
Definition: samplerState.h:36
Texture::string_compression_mode
static CompressionMode string_compression_mode(const std::string &str)
Returns the CompressionMode value associated with the given string representation.
Definition: texture.cxx:2403
SamplerState::get_minfilter
get_minfilter
Returns the filter mode of the texture for minification.
Definition: samplerState.h:115
Texture::string_quality_level
static QualityLevel string_quality_level(const std::string &str)
Returns the QualityLevel value associated with the given string representation.
Definition: texture.cxx:2466
DatagramIterator::get_bool
bool get_bool()
Extracts a boolean value.
Definition: datagramIterator.I:48
InternalName::get_name
get_name
Returns the complete name represented by the InternalName and all of its parents.
Definition: internalName.h:61
VirtualFileSystem::open_write_file
std::ostream * open_write_file(const Filename &filename, bool auto_wrap, bool truncate)
Convenience function; returns a newly allocated ostream if the file exists and can be written,...
Definition: virtualFileSystem.cxx:891
FactoryParams
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
Texture::read_txo
bool read_txo(std::istream &in, const std::string &filename="")
Reads the texture from a Panda texture object.
Definition: texture.cxx:840
CycleDataWriter
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
Definition: cycleDataWriter.h:34
TexturePool::load_texture
static Texture * load_texture(const Filename &filename, int primary_file_num_channels=0, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads the given filename up into a texture, if it has not already been loaded, and returns the new te...
Definition: texturePool.I:47
Texture::string_component_type
static ComponentType string_component_type(const std::string &str)
Returns the ComponentType corresponding to the indicated string word.
Definition: texture.cxx:2133
Texture::finalize
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
Definition: texture.cxx:9858
PNMImageHeader::get_y_size
int get_y_size() const
Returns the number of pixels in the Y direction.
Definition: pnmImageHeader.I:153
Texture::get_expected_ram_page_size
get_expected_ram_page_size
Returns the number of bytes that should be used per each Z page of the 3-d texture.
Definition: texture.h:441
VirtualFileSystem
A hierarchy of directories and files that appears to be one continuous file system,...
Definition: virtualFileSystem.h:40
Texture::set_aux_data
set_aux_data
Records an arbitrary object in the Texture, associated with a specified key.
Definition: texture.h:544
PNMImage::get_read_y_size
int get_read_y_size() const
Returns the requested y_size of the image if set_read_size() has been called, or the image y_size oth...
Definition: pnmImage.I:324
BamReader::read_object
TypedWritable * read_object()
Reads a single object from the Bam file.
Definition: bamReader.cxx:224
BamCache::store
bool store(BamCacheRecord *record)
Flushes a cache entry to disk.
Definition: bamCache.cxx:188
LoaderOptions::set_auto_texture_scale
set_auto_texture_scale
Set this flag to ATS_none, ATS_up, ATS_down, or ATS_pad to control how a texture is scaled from disk ...
Definition: loaderOptions.h:69
CycleDataReader
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
Definition: cycleDataReader.h:35
Texture::has_binary_alpha
static bool has_binary_alpha(Format format)
Returns true if the indicated format includes a binary alpha only, false otherwise.
Definition: texture.cxx:2602
Texture::adjust_size
static bool adjust_size(int &x_size, int &y_size, const std::string &name, bool for_padding, AutoTextureScale auto_texture_scale=ATS_unspecified)
Computes the proper size of the texture, based on the original size, the filename,...
Definition: texture.cxx:2640
Texture::has_ram_image
bool has_ram_image() const
Returns true if the Texture has its image contents available in main RAM, false if it exists only in ...
Definition: texture.I:1242
Filename::find_on_searchpath
int find_on_searchpath(const DSearchPath &searchpath)
Performs the reverse of the resolve_filename() operation: assuming that the current filename is fully...
Definition: filename.cxx:1689
PStatCollector
A lightweight class that represents a single element that may be timed and/or counted via stats.
Definition: pStatCollector.h:43
ConstPointerToArray::cast_non_const
PointerToArray< Element > cast_non_const() const
Casts away the constness of the CPTA(Element), and returns an equivalent PTA(Element).
Definition: pointerToArray.I:927
datagramOutputFile.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CullTraverserData
This collects together the pieces of data that are accumulated for each node while walking the scene ...
Definition: cullTraverserData.h:40
DatagramOutputFile::write_header
bool write_header(const std::string &header)
Writes a sequence of bytes to the beginning of the datagram file.
Definition: datagramOutputFile.cxx:96
Texture::get_ram_mipmap_image
CPTA_uchar get_ram_mipmap_image(int n) const
Returns the system-RAM image data associated with the nth mipmap level, if present.
Definition: texture.cxx:1208
BamReader::get_filename
get_filename
If a BAM is a file, then the BamReader should contain the name of the file.
Definition: bamReader.h:155
Namable::has_name
bool has_name() const
Returns true if the Namable has a nonempty name set, false if the name is empty.
Definition: namable.I:44
Datagram::add_int16
void add_int16(int16_t value)
Adds a signed 16-bit integer to the datagram.
Definition: datagram.I:58
PNMImage::get_channel_val
xelval get_channel_val(int x, int y, int channel) const
Returns the nth component color at the indicated pixel.
Definition: pnmImage.cxx:837
MutexDirect::unlock
void unlock()
Alias for release() to match C++11 semantics.
Definition: mutexDirect.I:39
PreparedGraphicsObjects::prepare_texture_now
TextureContext * prepare_texture_now(Texture *tex, int view, GraphicsStateGuardianBase *gsg)
Immediately creates a new TextureContext for the indicated texture and returns it.
Definition: preparedGraphicsObjects.cxx:367
Texture::set_ram_mipmap_pointer
void set_ram_mipmap_pointer(int n, void *image, size_t page_size=0)
Sets an explicit void pointer as the texture's mipmap image for the indicated level.
Definition: texture.cxx:1241
datagram.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Texture::get_num_loadable_ram_mipmap_images
get_num_loadable_ram_mipmap_images
Returns the number of contiguous mipmap levels that exist in RAM, up until the first gap in the seque...
Definition: texture.h:494
PNMReader
This is an abstract base class that defines the interface for reading image files of various types.
Definition: pnmReader.h:27
DatagramOutputFile
This class can be used to write a binary file that consists of an arbitrary header followed by a numb...
Definition: datagramOutputFile.h:30
Texture::get_ram_mipmap_pointer
void * get_ram_mipmap_pointer(int n) const
Similiar to get_ram_mipmap_image(), however, in this case the void pointer for the given ram image is...
Definition: texture.cxx:1222
Texture::uses_mipmaps
bool uses_mipmaps() const
Returns true if the minfilter settings on this texture indicate the use of mipmapping,...
Definition: texture.I:1127
Texture::get_data_size_bytes
size_t get_data_size_bytes(PreparedGraphicsObjects *prepared_objects) const
Returns the number of bytes which the texture is reported to consume within graphics memory,...
Definition: texture.cxx:1486
PipelineCycler::elevate_read_upstream
CycleDataType * elevate_read_upstream(const CycleDataType *pointer, bool force_to_0, Thread *current_thread)
See PipelineCyclerBase::elevate_read_upstream().
Definition: pipelineCycler.I:276
Factory::register_factory
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
Texture::cull_callback
virtual bool cull_callback(CullTraverser *trav, const CullTraverserData &data) const
If has_cull_callback() returns true, this function will be called during the cull traversal to perfor...
Definition: texture.cxx:2529
ReferenceCount::ref
void ref() const
Explicitly increments the reference count.
Definition: referenceCount.I:151
ConfigVariableList::get_num_unique_values
size_t get_num_unique_values() const
Returns the number of unique values in the variable.
Definition: configVariableList.I:68
ConditionVarFullDirect::wait
void wait()
Waits on the condition.
Definition: conditionVarFullDirect.I:53
Texture::estimate_texture_memory
size_t estimate_texture_memory() const
Estimates the amount of texture memory that will be consumed by loading this texture.
Definition: texture.cxx:675
PNMImageHeader::has_alpha
static bool has_alpha(ColorType color_type)
This static variant of has_alpha() returns true if the indicated image type includes an alpha channel...
Definition: pnmImageHeader.I:106
Datagram::add_int32
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
Definition: datagram.I:67
BamReader::resolve
bool resolve()
This may be called at any time during processing of the Bam file to resolve all the known pointers so...
Definition: bamReader.cxx:325
convert_srgb.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Texture::get_resident
bool get_resident(PreparedGraphicsObjects *prepared_objects) const
Returns true if this Texture is reported to be resident within graphics memory for the indicated GSG.
Definition: texture.cxx:1540
BufferContext::get_active
get_active
Returns the active flag associated with this object.
Definition: bufferContext.h:55
texturePeeker.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PNMImage::get_gray
float get_gray(int x, int y) const
Returns the gray component color at the indicated pixel.
Definition: pnmImage.I:799
PNMImageHeader::is_grayscale
static bool is_grayscale(ColorType color_type)
This static variant of is_grayscale() returns true if the indicated image type represents a grayscale...
Definition: pnmImageHeader.I:86
BamCacheRecord::get_data
get_data
Returns a pointer to the data stored in the record, or NULL if there is no data.
Definition: bamCacheRecord.h:77
pixel
Definition: pnmimage_base.h:41
DatagramIterator::get_remaining_size
size_t get_remaining_size() const
Return the bytes left in the datagram.
Definition: datagramIterator.I:454
Texture::set_ram_image_as
void set_ram_image_as(CPTA_uchar image, const std::string &provided_format)
Replaces the current system-RAM image with the new data, converting it first if necessary from the in...
Definition: texture.cxx:1020
SamplerState::get_wrap_w
get_wrap_w
Returns the wrap mode of the texture in the W direction.
Definition: samplerState.h:114
Filename::pattern_filename
static Filename pattern_filename(const std::string &filename)
Constructs a filename that represents a sequence of numbered files.
Definition: filename.I:160
Texture::down_to_power_2
static int down_to_power_2(int value)
Returns the largest power of 2 less than or equal to value.
Definition: texture.cxx:1995
BamCache
This class maintains a cache of Bam and/or Txo objects generated from model files and texture images ...
Definition: bamCache.h:42
PNMImage::get_gray_val
xelval get_gray_val(int x, int y) const
Returns the gray component color at the indicated pixel.
Definition: pnmImage.I:484
PNMImage::add_alpha
void add_alpha()
Adds an alpha channel to the image, if it does not already have one.
Definition: pnmImage.I:363
CycleDataLockedReader
This template class calls PipelineCycler::read() in the constructor and PipelineCycler::release_read(...
Definition: cycleDataLockedReader.h:40
PreparedGraphicsObjects::is_texture_queued
bool is_texture_queued(const Texture *tex) const
Returns true if the texture has been queued on this GSG, false otherwise.
Definition: preparedGraphicsObjects.cxx:221
VirtualFileSystem::get_global_ptr
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
Definition: virtualFileSystem.cxx:742
virtualFileSystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PNMImage::alpha_fill
void alpha_fill(float alpha=0.0)
Sets the entire alpha channel to the given level.
Definition: pnmImage.I:272
Datagram::add_uint32
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
texture.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
BamCache::get_cache_compressed_textures
get_cache_compressed_textures
Returns whether compressed texture files will be stored in the cache, as compressed txo files.
Definition: bamCache.h:92
PNMImage::set_blue
void set_blue(int x, int y, float b)
Sets the blue component color only at the indicated pixel.
Definition: pnmImage.I:836
VirtualFileSystem::close_read_file
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
Definition: virtualFileSystem.cxx:867
streamReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VirtualFile
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
Namable
A base class for all things which can have a name.
Definition: namable.h:26
zStream.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Datagram::add_bool
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
ReferenceCount::local_object
void local_object()
This function should be called, once, immediately after creating a new instance of some ReferenceCoun...
Definition: referenceCount.I:229
StreamReader
A class to read sequential binary data directly from an istream.
Definition: streamReader.h:28
ReferenceCount::unref
virtual bool unref() const
Explicitly decrements the reference count.
Definition: referenceCount.I:179
PNMImage::get_alpha
float get_alpha(int x, int y) const
Returns the alpha component color at the indicated pixel.
Definition: pnmImage.I:809
PNMImage::get_blue_val
xelval get_blue_val(int x, int y) const
Returns the blue component color at the indicated pixel.
Definition: pnmImage.I:472
Texture::was_image_modified
bool was_image_modified(PreparedGraphicsObjects *prepared_objects) const
Returns true if the texture needs to be re-loaded onto the indicated GSG, either because its image da...
Definition: texture.cxx:1454
Texture::generate_normalization_cube_map
void generate_normalization_cube_map(int size)
Generates a special cube map image in the texture that can be used to apply bump mapping effects: for...
Definition: texture.cxx:424
Filename::make_absolute
void make_absolute()
Converts the filename to a fully-qualified pathname from the root (if it is a relative pathname),...
Definition: filename.cxx:968
TextureContext
This is a special class object that holds all the information returned by a particular GSG to indicat...
Definition: textureContext.h:33
DatagramIterator::extract_bytes
vector_uchar extract_bytes(size_t size)
Extracts the indicated number of bytes in the datagram and returns them as a string.
Definition: datagramIterator.cxx:126
PNMImage::fill
void fill(float red, float green, float blue)
Sets the entire image (except the alpha channel) to the given color.
Definition: pnmImage.I:246
TexturePool::load_3d_texture
static Texture * load_3d_texture(const Filename &filename_pattern, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads a 3-D texture that is specified with a series of n pages, all numbered in sequence,...
Definition: texturePool.I:84
Texture::get_keep_ram_image
get_keep_ram_image
Returns the flag that indicates whether this Texture is eligible to have its main RAM copy of the tex...
Definition: texture.h:464
GraphicsStateGuardianBase
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
Definition: graphicsStateGuardianBase.h:110
Texture::get_ram_image_as
CPTA_uchar get_ram_image_as(const std::string &requested_format)
Returns the uncompressed system-RAM image data associated with the texture.
Definition: texture.cxx:7228
TexturePeeker
An instance of this object is returned by Texture::peek().
Definition: texturePeeker.h:27
Filename::set_basename_wo_extension
void set_basename_wo_extension(const std::string &s)
Replaces the basename part of the filename, without the file extension.
Definition: filename.cxx:783
BamReader::get_file_minor_ver
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
PNMImage::write
bool write(const Filename &filename, PNMFileType *type=nullptr) const
Writes the image to the indicated filename.
Definition: pnmImage.cxx:385
Texture::format_component_type
static std::string format_component_type(ComponentType ct)
Returns the indicated ComponentType converted to a string word.
Definition: texture.cxx:2104
datagramIterator.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PfmFile::store
bool store(PNMImage &pnmimage) const
Copies the data to the indicated PNMImage, converting to RGB values.
Definition: pfmFile.cxx:360
ConfigVariableInt::get_word
int get_word(size_t n) const
Returns the variable's nth value.
Definition: configVariableInt.I:129
indent.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Texture::format_compression_mode
static std::string format_compression_mode(CompressionMode cm)
Returns the indicated CompressionMode converted to a string word.
Definition: texture.cxx:2361
Texture::release
bool release(PreparedGraphicsObjects *prepared_objects)
Frees the texture context only on the indicated object, if it exists there.
Definition: texture.cxx:1567
BamCacheRecord::add_dependent_file
void add_dependent_file(const Filename &pathname)
Adds the indicated file to the list of files that will be loaded to generate the data in this record.
Definition: bamCacheRecord.cxx:147
VirtualFileSystem::resolve_filename
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
Definition: virtualFileSystem.cxx:641
DatagramIterator::get_uint32
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
Definition: datagramIterator.I:164
DatagramIterator::get_uint8
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
Definition: datagramIterator.I:72
PNMImage::set_green
void set_green(int x, int y, float g)
Sets the green component color only at the indicated pixel.
Definition: pnmImage.I:827
LoaderOptions::get_texture_num_views
get_texture_num_views
See set_texture_num_views().
Definition: loaderOptions.h:64
TextureContext::was_image_modified
bool was_image_modified() const
Returns true if the texture image has been modified since the last time mark_loaded() was called.
Definition: textureContext.I:66
Texture::has_simple_ram_image
has_simple_ram_image
Returns true if the Texture has a "simple" image available in main RAM.
Definition: texture.h:509
bamWriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Texture::generate_simple_ram_image
void generate_simple_ram_image()
Computes the "simple" ram image by loading the main RAM image, if it is not already available,...
Definition: texture.cxx:1318
SamplerState::get_wrap_u
get_wrap_u
Returns the wrap mode of the texture in the U direction.
Definition: samplerState.h:112
Filename::make_relative_to
bool make_relative_to(Filename directory, bool allow_backups=true)
Adjusts this filename, which must be a fully-specified pathname beginning with a slash,...
Definition: filename.cxx:1640
Texture::set_size_padded
void set_size_padded(int x=1, int y=1, int z=1)
Changes the size of the texture, padding if necessary, and setting the pad region as well.
Definition: texture.cxx:1907
PNMImage::get_read_x_size
int get_read_x_size() const
Returns the requested x_size of the image if set_read_size() has been called, or the image x_size oth...
Definition: pnmImage.I:315
Texture::ensure_loader_type
virtual void ensure_loader_type(const Filename &filename)
May be called prior to calling read_txo() or any bam-related Texture- creating callback,...
Definition: texture.cxx:2750
TypedObject::is_exact_type
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition: typedObject.I:38
PipelineCycler::read
const CycleDataType * read(Thread *current_thread) const
See PipelineCyclerBase::read().
Definition: pipelineCycler.I:240
BamWriter::get_filename
get_filename
If a BAM is a file, then the BamWriter should contain the name of the file.
Definition: bamWriter.h:92
Thread
A thread; that is, a lightweight process.
Definition: thread.h:46
DatagramOutputFile::open
bool open(const FileReference *file)
Opens the indicated filename for writing.
Definition: datagramOutputFile.cxx:28
PNMImage::set_read_size
void set_read_size(int x_size, int y_size)
Specifies the size to we'd like to scale the image upon reading it.
Definition: pnmImage.I:288
PfmFile::set_channel
void set_channel(int x, int y, int c, PN_float32 value)
Replaces the cth channel of the point value at the indicated point.
Definition: pfmFile.I:63
Datagram::append_data
void append_data(const void *data, size_t size)
Appends some more raw data to the end of the datagram.
Definition: datagram.cxx:129
Filename::get_basename
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
Texture::clear_aux_data
clear_aux_data
Removes a record previously recorded via set_aux_data().
Definition: texture.h:544
Texture::clear_ram_mipmap_image
void clear_ram_mipmap_image(int n)
Discards the current system-RAM image for the nth mipmap level.
Definition: texture.cxx:1271
Texture::generate_alpha_scale_map
void generate_alpha_scale_map()
Generates a special 256x1 1-d texture that can be used to apply an arbitrary alpha scale to objects b...
Definition: texture.cxx:526
PNMImageHeader::make_reader
PNMReader * make_reader(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true) const
Returns a newly-allocated PNMReader of the suitable type for reading from the indicated image filenam...
Definition: pnmImageHeader.cxx:80
GlobPattern
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
ConditionVarFullDirect::notify_all
void notify_all()
Informs all of the other threads who are currently blocked on wait() that the relevant condition has ...
Definition: conditionVarFullDirect.I:100
ReferenceCount::get_ref_count
get_ref_count
Returns the current reference count.
Definition: referenceCount.h:53
parse_params
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
PNMImage::take_from
void take_from(PNMImage &orig)
Move the contents of the other image into this one, and empty the other image.
Definition: pnmImage.cxx:224
Texture::format_texture_type
static std::string format_texture_type(TextureType tt)
Returns the indicated TextureType converted to a string word.
Definition: texture.cxx:2052
SamplerState::get_wrap_v
get_wrap_v
Returns the wrap mode of the texture in the V direction.
Definition: samplerState.h:113
Filename::has_hash
bool has_hash() const
Returns true if the filename is indicated to be a filename pattern (that is, set_pattern(true) was ca...
Definition: filename.I:531
PNMImageHeader::get_type
get_type
If the file type is known (e.g.
Definition: pnmImageHeader.h:85
PNMReader::is_floating_point
virtual bool is_floating_point()
Returns true if this PNMFileType represents a floating-point image type, false if it is a normal,...
Definition: pnmReader.cxx:71
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
Texture::read
bool read(const Filename &fullpath, const LoaderOptions &options=LoaderOptions())
Reads the named filename into the texture.
Definition: texture.cxx:551
TypedObject::is_of_type
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
Texture::Texture
Texture(const std::string &name=std::string())
Constructs an empty texture.
Definition: texture.cxx:374
Texture::has_cull_callback
virtual bool has_cull_callback() const
Should be overridden by derived classes to return true if cull_callback() has been defined.
Definition: texture.cxx:2515
encode_sRGB_uchar
EXPCL_PANDA_PNMIMAGE unsigned char encode_sRGB_uchar(unsigned char val)
Encodes the linearized unsigned char value to an sRGB-encoded unsigned char value.
Definition: convert_srgb.I:80
Texture::is_specific
static bool is_specific(CompressionMode compression)
Returns true if the indicated compression mode is one of the specific compression types,...
Definition: texture.cxx:2557