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::
1176 get_num_loadable_ram_mipmap_images() const {
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();
10016  me.add_bool(has_simple_ram_image);
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 }
Texture(const std::string &name=std::string())
Constructs an empty texture.
Definition: texture.cxx:374
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_minfilter
Returns the filter mode of the texture for minification.
Definition: samplerState.h:115
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
static bool is_srgb(Format format)
Returns true if the indicated format is in the sRGB color space, false otherwise.
Definition: texture.cxx:2617
void add_int16(int16_t value)
Adds a signed 16-bit integer to the datagram.
Definition: datagram.I:58
static std::string format_quality_level(QualityLevel tql)
Returns the indicated QualityLevel converted to a string word.
Definition: texture.cxx:2446
bool was_image_modified() const
Returns true if the texture image has been modified since the last time mark_loaded() was called.
get_wrap_u
Returns the wrap mode of the texture in the U direction.
Definition: samplerState.h:112
get_ref_count
Returns the current reference count.
set_data
Stores a new data object on the record.
void set_green(int x, int y, float g)
Sets the green component color only at the indicated pixel.
Definition: pnmImage.I:813
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition: typedObject.I:38
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static std::string format_component_type(ComponentType ct)
Returns the indicated ComponentType converted to a string word.
Definition: texture.cxx:2104
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class represents a thread-safe handle to a promised future result of an asynchronous operation,...
Definition: asyncFuture.h:61
void copy_sub_image(const PNMImage &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1)
Copies a rectangular area of another image into a rectangular area of this image.
Definition: pnmImage.cxx:1077
static CompressionMode string_compression_mode(const std::string &str)
Returns the CompressionMode value associated with the given string representation.
Definition: texture.cxx:2403
get_active
Returns the active flag associated with this object.
Definition: bufferContext.h:55
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...
xelval get_channel_val(int x, int y, int channel) const
Returns the nth component color at the indicated pixel.
Definition: pnmImage.cxx:837
bool get_bool()
Extracts a boolean value.
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
static Format string_format(const std::string &str)
Returns the Format corresponding to the indicated string word.
Definition: texture.cxx:2265
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
ColorSpace get_color_space() const
Returns the color space in which the image is encoded.
Definition: pnmImage.I:243
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void release_texture(TextureContext *tc)
Indicates that a texture context, created by a previous call to prepare_texture(),...
get_num_channels
Returns the number of channels in the image.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_cache_textures
Returns whether texture files (e.g.
Definition: bamCache.h:90
xelval get_gray_val(int x, int y) const
Returns the gray component color at the indicated pixel.
Definition: pnmImage.I:395
void wait()
Waits on the condition.
static std::string format_format(Format f)
Returns the indicated Format converted to a string word.
Definition: texture.cxx:2163
void append_data(const void *data, size_t size)
Appends some more raw data to the end of the datagram.
Definition: datagram.cxx:129
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
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
size_t get_num_unique_values() const
Returns the number of unique values in the variable.
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
void alpha_fill(float alpha=0.0)
Sets the entire alpha channel to the given level.
Definition: pnmImage.I:183
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
Specifies parameters that may be passed to the loader.
Definition: loaderOptions.h:23
This class maintains a cache of Bam and/or Txo objects generated from model files and texture images ...
Definition: bamCache.h:42
get_file_texture_mode
Returns the BamTextureMode preference indicated by the Bam file currently being written.
Definition: bamWriter.h:95
A hierarchy of directories and files that appears to be one continuous file system,...
bool dequeue_texture(Texture *tex)
Removes a texture from the queued list of textures to be prepared.
TypedWritable * read_object()
Reads a single object from the Bam file.
Definition: bamReader.cxx:224
This class can be used to read a binary file that consists of an arbitrary header followed by a numbe...
int release_all()
Frees the context allocated on all objects for which the texture has been declared.
Definition: texture.cxx:1593
float get_gray(int x, int y) const
Returns the gray component color at the indicated pixel.
Definition: pnmImage.I:785
static void register_with_read_factory()
Factory method to generate a Texture object.
Definition: texture.cxx:9830
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:47
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
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
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
bool read_header(std::string &header, size_t num_bytes)
Reads a sequence of bytes from the beginning of the datagram file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool write(const Filename &filename, PNMFileType *type=nullptr) const
Writes the image to the indicated filename.
Definition: pnmImage.cxx:385
bool write_header(const std::string &header)
Writes a sequence of bytes to the beginning of the datagram file.
static QualityLevel string_quality_level(const std::string &str)
Returns the QualityLevel value associated with the given string representation.
Definition: texture.cxx:2466
size_t estimate_texture_memory() const
Estimates the amount of texture memory that will be consumed by loading this texture.
Definition: texture.cxx:675
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
static bool is_specific(CompressionMode compression)
Returns true if the indicated compression mode is one of the specific compression types,...
Definition: texture.cxx:2557
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
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:278
This is a special class object that holds all the information returned by a particular GSG to indicat...
This collects together the pieces of data that are accumulated for each node while walking the scene ...
A base class for things which need to inherit from both TypedObject and from ReferenceCount.
get_wrap_w
Returns the wrap mode of the texture in the W direction.
Definition: samplerState.h:114
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
PTA_uchar modify_simple_ram_image()
Returns a modifiable pointer to the internal "simple" texture image.
Definition: texture.cxx:1286
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_next_higher_bit(uint16_t x)
Returns the smallest power of 2 greater than x.
Definition: pbitops.I:244
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being written.
Definition: bamWriter.I:59
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
xel * get_array()
Directly access the underlying PNMImage array.
Definition: pnmImage.I:1084
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
int get_lowest_on_bit(uint16_t x)
Returns the index of the lowest 1 bit in the word.
Definition: pbitops.I:129
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:25
get_aux_data
Returns a record previously recorded via set_aux_data().
Definition: texture.h:544
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_red(int x, int y, float r)
Sets the red component color only at the indicated pixel.
Definition: pnmImage.I:804
int32_t get_int32()
Extracts a signed 32-bit integer.
void unlock()
Alias for release() to match C++11 semantics.
Definition: mutexDirect.I:39
bool open(const FileReference *file)
Opens the indicated filename for reading.
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
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
A table of objects that are saved within the graphics context for reference by handle later.
size_t get_remaining_size() const
Return the bytes left in the datagram.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_name
Returns the complete name represented by the InternalName and all of its parents.
Definition: internalName.h:61
std::string get_string()
Extracts a variable-length string.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
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
int get_y_size() const
Returns the number of pixels in the Y direction.
void quick_filter_from(const PNMImage &copy, int xborder=0, int yborder=0)
Resizes from the given image, with a fixed radius of 0.5.
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
bool open(const FileReference *file)
Opens the indicated filename for writing.
int get_x_size() const
Returns the number of pixels in the X direction.
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
bool is_valid() const
Returns true if the image has been read in or correctly initialized with a height and width.
Definition: pnmImage.I:253
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
void notify_all()
Informs all of the other threads who are currently blocked on wait() that the relevant condition has ...
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
bool release(PreparedGraphicsObjects *prepared_objects)
Frees the texture context only on the indicated object, if it exists there.
Definition: texture.cxx:1567
PT(Texture) Texture
Constructs a new Texture object from the txo file.
Definition: texture.cxx:854
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
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
static TextureType string_texture_type(const std::string &str)
Returns the TextureType corresponding to the indicated string word.
Definition: texture.cxx:2078
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
bool store(BamCacheRecord *record)
Flushes a cache entry to disk.
Definition: bamCache.cxx:188
bool read(const Filename &fullpath)
Reads the PFM data from the indicated file, returning true on success, false on failure.
Definition: pfmFile.cxx:121
A lightweight class that represents a single element that may be timed and/or counted via stats.
get_wrap_v
Returns the wrap mode of the texture in the V direction.
Definition: samplerState.h:113
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
vector_uchar extract_bytes(size_t size)
Extracts the indicated number of bytes in the datagram and returns them as a string.
bool write(const Filename &fullpath)
Writes the PFM data to the indicated file, returning true on success, false on failure.
Definition: pfmFile.cxx:204
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
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
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
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
A base class for all things which can have a name.
Definition: namable.h:26
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
bool uses_mipmaps() const
Returns true if the minfilter settings on this texture indicate the use of mipmapping,...
Definition: texture.I:1127
get_maxval
Returns the maximum channel value allowable for any pixel in this image; for instance,...
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
bool init()
Initializes the BamReader prior to reading any objects from its source.
Definition: bamReader.cxx:85
clear_aux_data
Removes a record previously recorded via set_aux_data().
Definition: texture.h:544
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:235
get_filename
If a BAM is a file, then the BamWriter should contain the name of the file.
Definition: bamWriter.h:92
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
PointerToArray< Element > cast_non_const() const
Casts away the constness of the CPTA(Element), and returns an equivalent PTA(Element).
Defines a pfm file, a 2-d table of floating-point numbers, either 3-component or 1-component,...
Definition: pfmFile.h:31
static bool has_alpha(ColorType color_type)
This static variant of has_alpha() returns true if the indicated image type includes an alpha channel...
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
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
static bool has_alpha(Format format)
Returns true if the indicated format includes alpha, false otherwise.
Definition: texture.cxx:2573
get_anisotropic_degree
Returns the degree of anisotropic filtering that should be applied to the texture.
Definition: samplerState.h:119
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
void copy_header_from(const PNMImageHeader &header)
Copies just the header information into this image.
Definition: pnmImage.cxx:200
xelval get_blue_val(int x, int y) const
Returns the blue component color at the indicated pixel.
Definition: pnmImage.I:383
void set_blue(int x, int y, float b)
Sets the blue component color only at the indicated pixel.
Definition: pnmImage.I:822
This template class calls PipelineCycler::read() in the constructor and PipelineCycler::release_read(...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
static GraphicsStateGuardianBase * get_default_gsg()
Returns a pointer to the "default" GSG.
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
int get_word(size_t n) const
Returns the variable's nth value.
get_data
Returns a pointer to the data stored in the record, or NULL if there is no data.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
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
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
get_loader_options
Returns the LoaderOptions passed to the loader when the model was requested, if any.
Definition: bamReader.h:156
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
This class specializes ConfigVariable as an enumerated type.
set_aux_data
Records an arbitrary object in the Texture, associated with a specified key.
Definition: texture.h:544
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
get_texture_num_views
See set_texture_num_views().
Definition: loaderOptions.h:64
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
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
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
std::string get_unique_value(size_t n) const
Returns the nth unique value of the variable.
xelval * get_alpha_array()
Directly access the underlying PNMImage array of alpha values.
Definition: pnmImage.I:1101
static ComponentType string_component_type(const std::string &str)
Returns the ComponentType corresponding to the indicated string word.
Definition: texture.cxx:2133
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
This is an abstract base class that defines the interface for reading image files of various types.
Definition: pnmReader.h:27
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
void ref() const
Explicitly increments the reference count.
bool make_relative_to(Filename directory, bool allow_backups=true)
Adjusts this filename, which must be a fully-specified pathname beginning with a slash,...
Definition: filename.cxx:1640
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
bool is_texture_queued(const Texture *tex) const
Returns true if the texture has been queued on this GSG, false otherwise.
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
An instance of this object is returned by Texture::peek().
Definition: texturePeeker.h:27
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
Represents a set of settings that indicate how a texture is sampled.
Definition: samplerState.h:36
void local_object()
This function should be called, once, immediately after creating a new instance of some ReferenceCoun...
static void close_write_file(std::ostream *stream)
Closes a file opened by a previous call to open_write_file().
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
bool load(const PNMImage &pnmimage)
Fills the PfmFile with the data from the indicated PNMImage, converted to floating-point values.
Definition: pfmFile.cxx:287
void set_basename_wo_extension(const std::string &s)
Replaces the basename part of the filename, without the file extension.
Definition: filename.cxx:783
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static bool is_unsigned(ComponentType ctype)
Returns true if the indicated component type is unsigned, false otherwise.
Definition: texture.cxx:2545
This class can be used to write a binary file that consists of an arbitrary header followed by a numb...
void add_alpha()
Adds an alpha channel to the image, if it does not already have one.
Definition: pnmImage.I:274
void make_absolute()
Converts the filename to a fully-qualified pathname from the root (if it is a relative pathname),...
Definition: filename.cxx:968
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
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,...
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
CycleDataType * elevate_read_upstream(const CycleDataType *pointer, bool force_to_0, Thread *current_thread)
See PipelineCyclerBase::elevate_read_upstream().
void clear()
Frees all memory allocated for the image, and clears all its parameters (size, color,...
Definition: pnmImage.cxx:48
static int up_to_power_2(int value)
Returns the smallest power of 2 greater than or equal to value.
Definition: texture.cxx:1983
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
Definition: datagram.I:67
void set_alpha(int x, int y, float a)
Sets the alpha component color only at the indicated pixel.
Definition: pnmImage.I:845
get_magfilter
Returns the filter mode of the texture for magnification.
Definition: samplerState.h:116
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
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
A thread; that is, a lightweight process.
Definition: thread.h:46
void take_from(PNMImage &orig)
Move the contents of the other image into this one, and empty the other image.
Definition: pnmImage.cxx:224
string upcase(const string &s)
Returns the input string with all lowercase letters converted to uppercase.
TextureContext * prepare_texture_now(Texture *tex, int view, GraphicsStateGuardianBase *gsg)
Immediately creates a new TextureContext for the indicated texture and returns it.
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
get_border_color
Returns the solid color of the texture's border.
Definition: samplerState.h:121
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
CycleDataType * write_upstream(bool force_to_0, Thread *current_thread)
See PipelineCyclerBase::write_upstream().
static std::string format_texture_type(TextureType tt)
Returns the indicated TextureType converted to a string word.
Definition: texture.cxx:2052
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static int down_to_power_2(int value)
Returns the largest power of 2 less than or equal to value.
Definition: texture.cxx:1995
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
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
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
bool read(const Filename &fullpath, const LoaderOptions &options=LoaderOptions())
Reads the named filename into the texture.
Definition: texture.cxx:551
static bool is_grayscale(ColorType color_type)
This static variant of is_grayscale() returns true if the indicated image type represents a grayscale...
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
Filename get_cwd() const
Returns the current directory name.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_cache_compressed_textures
Returns whether compressed texture files will be stored in the cache, as compressed txo files.
Definition: bamCache.h:92
void clear_ram_mipmap_image(int n)
Discards the current system-RAM image for the nth mipmap level.
Definition: texture.cxx:1271
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.
bool read_txo(std::istream &in, const std::string &filename="")
Reads the texture from a Panda texture object.
Definition: texture.cxx:840
A class to retrieve the individual data elements previously stored in a Datagram.
void texture_uploaded()
This method is called by the GraphicsEngine at the beginning of the frame *after* a texture has been ...
Definition: texture.cxx:2492
bool has_name() const
Returns true if the Namable has a nonempty name set, false if the name is empty.
Definition: namable.I:44
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
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
has_simple_ram_image
Returns true if the Texture has a "simple" image available in main RAM.
Definition: texture.h:509
xelval get_alpha_val(int x, int y) const
Returns the alpha component color at the indicated pixel.
Definition: pnmImage.I:405
get_data_size_bytes
Returns the number of bytes previously reported for the data object.
Definition: bufferContext.h:53
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
static Filename pattern_filename(const std::string &filename)
Constructs a filename that represents a sequence of numbered files.
Definition: filename.I:160
void fill(float red, float green, float blue)
Sets the entire image (except the alpha channel) to the given color.
Definition: pnmImage.I:157
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
bool store(PNMImage &pnmimage) const
Copies the data to the indicated PNMImage, converting to RGB values.
Definition: pfmFile.cxx:360
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
void set_num_channels(int num_channels)
Changes the number of channels associated with the image.
Definition: pnmImage.I:264
A class to read sequential binary data directly from an istream.
Definition: streamReader.h:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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:199
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
const CycleDataType * read(Thread *current_thread) const
See PipelineCyclerBase::read().
get_filename
If a BAM is a file, then the BamReader should contain the name of the file.
Definition: bamReader.h:155
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
static bool has_binary_alpha(Format format)
Returns true if the indicated format includes a binary alpha only, false otherwise.
Definition: texture.cxx:2602
void lock()
Alias for acquire() to match C++11 semantics.
Definition: mutexDirect.I:19
void release_read(const CycleData *pointer) const
Releases a pointer previously obtained via a call to read().
get_auto_texture_scale
See set_auto_texture_scale().
Definition: loaderOptions.h:69
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static std::string format_compression_mode(CompressionMode cm)
Returns the indicated CompressionMode converted to a string word.
Definition: texture.cxx:2361
xelval get_green_val(int x, int y) const
Returns the green component color at the indicated pixel.
Definition: pnmImage.I:373
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
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
xelval get_red_val(int x, int y) const
Returns the red component color at the indicated pixel.
Definition: pnmImage.I:363
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
void clear()
Eliminates all data in the file.
Definition: pfmFile.cxx:77
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
bool write(const Filename &fullpath)
Writes the texture to the named filename.
Definition: texture.I:298
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
virtual bool unref() const
Explicitly decrements the reference count.
get_resident
Returns the resident flag associated with this object.
Definition: bufferContext.h:56
float get_alpha(int x, int y) const
Returns the alpha component color at the indicated pixel.
Definition: pnmImage.I:795
get_type
If the file type is known (e.g.
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:226
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
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.