Panda3D
texturePeeker.cxx
1 // Filename: texturePeeker.cxx
2 // Created by: drose (26Aug08)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "texturePeeker.h"
16 
17 
18 ////////////////////////////////////////////////////////////////////
19 // Function: TexturePeeker::Constructor
20 // Access: Private
21 // Description: Use Texture::peek() to construct a TexturePeeker.
22 //
23 // This constructor is called only by Texture::peek(),
24 // and assumes the texture's lock is already held.
25 ////////////////////////////////////////////////////////////////////
26 TexturePeeker::
27 TexturePeeker(Texture *tex, Texture::CData *cdata) {
28  if (cdata->_texture_type == Texture::TT_cube_map) {
29  // Cube map texture. We'll need to map from (u, v, w) to (u, v)
30  // within the appropriate page, where w indicates the page.
31 
32  // TODO: handle cube maps.
33  return;
34 
35  } else {
36  // Regular 1-d, 2-d, or 3-d texture. The coordinates map
37  // directly. Simple ram images are possible if it is a 2-d
38  // texture.
39  if (tex->do_has_ram_image(cdata) && cdata->_ram_image_compression == Texture::CM_off) {
40  // Get the regular RAM image if it is available.
41  _image = tex->do_get_ram_image(cdata);
42  _x_size = cdata->_x_size;
43  _y_size = cdata->_y_size;
44  _z_size = cdata->_z_size;
45  _component_width = cdata->_component_width;
46  _num_components = cdata->_num_components;
47  _format = cdata->_format;
48  _component_type = cdata->_component_type;
49 
50  } else if (!cdata->_simple_ram_image._image.empty()) {
51  // Get the simple RAM image if *that* is available.
52  _image = cdata->_simple_ram_image._image;
53  _x_size = cdata->_simple_x_size;
54  _y_size = cdata->_simple_y_size;
55  _z_size = 1;
56 
57  _component_width = 1;
58  _num_components = 4;
59  _format = Texture::F_rgba;
60  _component_type = Texture::T_unsigned_byte;
61 
62  } else {
63  // Failing that, reload and get the uncompressed RAM image.
64  _image = tex->do_get_uncompressed_ram_image(cdata);
65  _x_size = cdata->_x_size;
66  _y_size = cdata->_y_size;
67  _z_size = cdata->_z_size;
68  _component_width = cdata->_component_width;
69  _num_components = cdata->_num_components;
70  _format = cdata->_format;
71  _component_type = cdata->_component_type;
72  }
73  }
74 
75  if (_image.is_null()) {
76  return;
77  }
78  _pixel_width = _component_width * _num_components;
79 
80  switch (_component_type) {
81  case Texture::T_unsigned_byte:
82  _get_component = Texture::get_unsigned_byte;
83  break;
84 
85  case Texture::T_unsigned_short:
86  _get_component = Texture::get_unsigned_short;
87  break;
88 
89  default:
90  // Not supported.
91  _image.clear();
92  return;
93  }
94 
95  switch (_format) {
96  case Texture::F_depth_stencil:
97  case Texture::F_depth_component:
98 
99  case Texture::F_red:
100  _get_texel = get_texel_r;
101  break;
102 
103  case Texture::F_green:
104  _get_texel = get_texel_g;
105  break;
106 
107  case Texture::F_blue:
108  _get_texel = get_texel_b;
109  break;
110 
111  case Texture::F_alpha:
112  _get_texel = get_texel_a;
113  break;
114 
115  case Texture::F_luminance:
116  _get_texel = get_texel_l;
117  break;
118 
119  case Texture::F_luminance_alpha:
120  case Texture::F_luminance_alphamask:
121  _get_texel = get_texel_la;
122  break;
123 
124  case Texture::F_rgb:
125  case Texture::F_rgb5:
126  case Texture::F_rgb8:
127  case Texture::F_rgb12:
128  case Texture::F_rgb332:
129  _get_texel = get_texel_rgb;
130  break;
131 
132  case Texture::F_rgba:
133  case Texture::F_rgbm:
134  case Texture::F_rgba4:
135  case Texture::F_rgba5:
136  case Texture::F_rgba8:
137  case Texture::F_rgba12:
138  case Texture::F_rgba16:
139  case Texture::F_rgba32:
140  _get_texel = get_texel_rgba;
141  break;
142  default:
143  // Not supported.
144  _image.clear();
145  return;
146  }
147 }
148 
149 
150 ////////////////////////////////////////////////////////////////////
151 // Function: TexturePeeker::lookup
152 // Access: Published
153 // Description: Fills "color" with the RGBA color of the texel at
154 // point (u, v).
155 //
156 // The texel color is determined via nearest-point
157 // sampling (no filtering of adjacent pixels),
158 // regardless of the filter type associated with the
159 // texture. u, v, and w will wrap around regardless of
160 // the texture's wrap mode.
161 ////////////////////////////////////////////////////////////////////
162 void TexturePeeker::
163 lookup(LColor &color, PN_stdfloat u, PN_stdfloat v) const {
164  int x = int((u - cfloor(u)) * (PN_stdfloat)_x_size) % _x_size;
165  int y = int((v - cfloor(v)) * (PN_stdfloat)_y_size) % _y_size;
166 
167  nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size);
168  const unsigned char *p = _image.p() + (y * _x_size + x) * _pixel_width;
169 
170  (*_get_texel)(color, p, _get_component);
171 }
172 
173 ////////////////////////////////////////////////////////////////////
174 // Function: TexturePeeker::lookup
175 // Access: Published
176 // Description: Fills "color" with the RGBA color of the texel at
177 // point (u, v, w).
178 //
179 // The texel color is determined via nearest-point
180 // sampling (no filtering of adjacent pixels),
181 // regardless of the filter type associated with the
182 // texture. u, v, and w will wrap around regardless of
183 // the texture's wrap mode.
184 ////////////////////////////////////////////////////////////////////
185 void TexturePeeker::
186 lookup(LColor &color, PN_stdfloat u, PN_stdfloat v, PN_stdfloat w) const {
187  int x = int((u - cfloor(u)) * (PN_stdfloat)_x_size) % _x_size;
188  int y = int((v - cfloor(v)) * (PN_stdfloat)_y_size) % _y_size;
189  int z = int((w - cfloor(w)) * (PN_stdfloat)_z_size) % _z_size;
190 
191  nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size &&
192  z >= 0 && z < _z_size);
193  const unsigned char *p = _image.p() + (z * _x_size * _y_size + y * _x_size + x) * _pixel_width;
194 
195  (*_get_texel)(color, p, _get_component);
196 }
197 
198 ////////////////////////////////////////////////////////////////////
199 // Function: TexturePeeker::filter_rect
200 // Access: Published
201 // Description: Fills "color" with the average RGBA color of the
202 // texels within the rectangle defined by the specified
203 // coordinate range.
204 //
205 // The texel color is linearly filtered over the entire
206 // region. u, v, and w will wrap around regardless of
207 // the texture's wrap mode.
208 ////////////////////////////////////////////////////////////////////
209 void TexturePeeker::
211  PN_stdfloat min_u, PN_stdfloat min_v, PN_stdfloat max_u, PN_stdfloat max_v) const {
212  int min_x, max_x;
213  init_rect_minmax(min_x, max_x, min_u, max_u, _x_size);
214 
215  int min_y, max_y;
216  init_rect_minmax(min_y, max_y, min_v, max_v, _y_size);
217 
218  color.set(0.0f, 0.0f, 0.0f, 0.0f);
219  PN_stdfloat net = 0.0f;
220  accum_filter_y(color, net, 0,
221  min_x, max_x, min_u, max_u,
222  min_y, max_y, min_v, max_v,
223  1.0f);
224 
225  if (net != 0.0f) {
226  color /= net;
227  }
228 }
229 
230 ////////////////////////////////////////////////////////////////////
231 // Function: TexturePeeker::filter_rect
232 // Access: Published
233 // Description: Fills "color" with the average RGBA color of the
234 // texels within the rectangle defined by the specified
235 // coordinate range.
236 //
237 // The texel color is linearly filtered over the entire
238 // region. u, v, and w will wrap around regardless of
239 // the texture's wrap mode.
240 ////////////////////////////////////////////////////////////////////
241 void TexturePeeker::
243  PN_stdfloat min_u, PN_stdfloat min_v, PN_stdfloat min_w,
244  PN_stdfloat max_u, PN_stdfloat max_v, PN_stdfloat max_w) const {
245  int min_x, max_x;
246  init_rect_minmax(min_x, max_x, min_u, max_u, _x_size);
247 
248  int min_y, max_y;
249  init_rect_minmax(min_y, max_y, min_v, max_v, _y_size);
250 
251  int min_z, max_z;
252  init_rect_minmax(min_z, max_z, min_w, max_w, _z_size);
253 
254  color.set(0.0f, 0.0f, 0.0f, 0.0f);
255  PN_stdfloat net = 0.0f;
256  accum_filter_z(color, net,
257  min_x, max_x, min_u, max_u,
258  min_y, max_y, min_v, max_v,
259  min_z, max_z, min_w, max_w);
260 
261  if (net != 0.0f) {
262  color /= net;
263  }
264 }
265 
266 ////////////////////////////////////////////////////////////////////
267 // Function: TexturePeeker::init_rect_minmax
268 // Access: Private, Static
269 // Description: Sanity-checks min_u, max_u and computes min_x and
270 // min_y based on them. Also works for y and z.
271 ////////////////////////////////////////////////////////////////////
272 void TexturePeeker::
273 init_rect_minmax(int &min_x, int &max_x, PN_stdfloat &min_u, PN_stdfloat &max_u,
274  int x_size) {
275  if (min_u > max_u) {
276  PN_stdfloat t = min_u;
277  min_u = max_u;
278  max_u = t;
279  }
280  if (max_u - min_u >= 1.0f) {
281  min_u = 0.0f;
282  max_u = 1.0f;
283  }
284  min_x = (int)cfloor(min_u * (PN_stdfloat)x_size);
285  max_x = (int)cceil(max_u * (PN_stdfloat)x_size);
286  nassertv(min_x <= max_x);
287 }
288 
289 ////////////////////////////////////////////////////////////////////
290 // Function: TexturePeeker::accum_filter_z
291 // Access: Private
292 // Description: Accumulates the range of pixels from min_z to max_z.
293 ////////////////////////////////////////////////////////////////////
294 void TexturePeeker::
295 accum_filter_z(LColor &color, PN_stdfloat &net,
296  int min_x, int max_x, PN_stdfloat min_u, PN_stdfloat max_u,
297  int min_y, int max_y, PN_stdfloat min_v, PN_stdfloat max_v,
298  int min_z, int max_z, PN_stdfloat min_w, PN_stdfloat max_w) const {
299  nassertv(min_z >= 0 && min_z <= _z_size &&
300  max_z >= 0 && max_z <= _z_size);
301  int zi = min_z;
302 
303  if (min_z >= max_z - 1) {
304  // Within a single texel.
305  accum_filter_y(color, net, zi % _z_size,
306  min_x, max_x, min_u, max_u,
307  min_y, max_y, min_v, max_v,
308  1.0f);
309 
310  } else {
311  // First part-texel.
312  PN_stdfloat w = (min_z + 1) - min_w * _z_size;
313  accum_filter_y(color, net, zi % _z_size,
314  min_x, max_x, min_u, max_u,
315  min_y, max_y, min_v, max_v,
316  w);
317  int zs = max_z - 1;
318 
319  // Run of full texels.
320  zi = min_z + 1;
321  while (zi < zs) {
322  accum_filter_y(color, net, zi % _z_size,
323  min_x, max_x, min_u, max_u,
324  min_y, max_y, min_v, max_v,
325  1.0f);
326  ++zi;
327  }
328 
329  // Last part-texel.
330  w = max_w * _z_size - (max_z - 1);
331  accum_filter_y(color, net, zi % _z_size,
332  min_x, max_x, min_u, max_u,
333  min_y, max_y, min_v, max_v,
334  w);
335  }
336 }
337 
338 ////////////////////////////////////////////////////////////////////
339 // Function: TexturePeeker::accum_filter_y
340 // Access: Private
341 // Description: Accumulates the range of pixels from min_y to max_y.
342 ////////////////////////////////////////////////////////////////////
343 void TexturePeeker::
344 accum_filter_y(LColor &color, PN_stdfloat &net, int zi,
345  int min_x, int max_x, PN_stdfloat min_u, PN_stdfloat max_u,
346  int min_y, int max_y, PN_stdfloat min_v, PN_stdfloat max_v,
347  PN_stdfloat weight) const {
348  nassertv(zi >= 0 && zi < _z_size);
349  nassertv(min_y >= 0 && min_y <= _y_size &&
350  max_y >= 0 && max_y <= _y_size);
351  int yi = min_y;
352 
353  if (min_y >= max_y - 1) {
354  // Within a single texel.
355  accum_filter_x(color, net, yi % _y_size, zi, min_x, max_x, min_u, max_u, weight);
356 
357  } else {
358  // First part-texel.
359  PN_stdfloat w = (min_y + 1) - min_v * _y_size;
360  accum_filter_x(color, net, yi % _y_size, zi, min_x, max_x, min_u, max_u, weight * w);
361  int ys = max_y - 1;
362 
363  // Run of full texels.
364  yi = min_y + 1;
365  while (yi < ys) {
366  accum_filter_x(color, net, yi % _y_size, zi, min_x, max_x, min_u, max_u, weight);
367  ++yi;
368  }
369 
370  // Last part-texel.
371  w = max_v * _y_size - (max_y - 1);
372  accum_filter_x(color, net, yi % _y_size, zi, min_x, max_x, min_u, max_u, weight * w);
373  }
374 }
375 
376 ////////////////////////////////////////////////////////////////////
377 // Function: TexturePeeker::accum_filter_x
378 // Access: Private
379 // Description: Accumulates the range of pixels from min_x to max_x.
380 ////////////////////////////////////////////////////////////////////
381 void TexturePeeker::
382 accum_filter_x(LColor &color, PN_stdfloat &net, int yi, int zi,
383  int min_x, int max_x, PN_stdfloat min_u, PN_stdfloat max_u,
384  PN_stdfloat weight) const {
385  nassertv(yi >= 0 && yi < _y_size && zi >= 0 && zi < _z_size);
386  nassertv(min_x >= 0 && min_x <= _x_size &&
387  max_x >= 0 && max_x <= _x_size);
388 
389  // Compute the p corresponding to min_x.
390  int xi = min_x % _x_size;
391  const unsigned char *p = _image.p() + (zi * _x_size * _y_size + yi * _x_size + xi) * _pixel_width;
392 
393  if (min_x >= max_x - 1) {
394  // Within a single texel.
395  accum_texel(color, net, p, weight);
396 
397  } else {
398  // First part-texel.
399  PN_stdfloat w = (min_x + 1) - min_u * _x_size;
400  accum_texel(color, net, p, weight * w);
401  int xs = max_x - 1;
402 
403  // Run of full texels.
404  xi = min_x + 1;
405  while (xi < xs) {
406  if (xi == _x_size) {
407  xi = 0;
408  p = _image.p() + (zi * _x_size * _y_size + yi * _x_size + xi) * _pixel_width;
409  xs -= _x_size;
410  }
411  accum_texel(color, net, p, weight);
412  ++xi;
413  }
414 
415  // Last part-texel.
416  if (xi == _x_size) {
417  xi = 0;
418  p = _image.p() + (zi * _x_size * _y_size + yi * _x_size + xi) * _pixel_width;
419  }
420  w = max_u * _x_size - (max_x - 1);
421  accum_texel(color, net, p, weight * w);
422  }
423 }
424 
425 ////////////////////////////////////////////////////////////////////
426 // Function: TexturePeeker::accum_texel
427 // Access: Private
428 // Description: Accumulates a single texel into the total computed by
429 // filter_rect().
430 ////////////////////////////////////////////////////////////////////
431 void TexturePeeker::
432 accum_texel(LColor &color, PN_stdfloat &net, const unsigned char *&p, PN_stdfloat weight) const {
433  LColor c;
434  (*_get_texel)(c, p, _get_component);
435  color += c * weight;
436  net += weight;
437 }
438 
439 ////////////////////////////////////////////////////////////////////
440 // Function: TexturePeeker::get_texel_r
441 // Access: Private, Static
442 // Description: Gets the color of the texel at byte p, given that the
443 // texture is in format F_red.
444 ////////////////////////////////////////////////////////////////////
445 void TexturePeeker::
446 get_texel_r(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
447  color[0] = (*get_component)(p);
448  color[1] = 0.0f;
449  color[2] = 0.0f;
450  color[3] = 1.0f;
451 }
452 
453 ////////////////////////////////////////////////////////////////////
454 // Function: TexturePeeker::get_texel_g
455 // Access: Private, Static
456 // Description: Gets the color of the texel at byte p, given that the
457 // texture is in format F_green.
458 ////////////////////////////////////////////////////////////////////
459 void TexturePeeker::
460 get_texel_g(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
461  color[0] = 0.0f;
462  color[1] = (*get_component)(p);
463  color[2] = 0.0f;
464  color[3] = 1.0f;
465 }
466 
467 ////////////////////////////////////////////////////////////////////
468 // Function: TexturePeeker::get_texel_b
469 // Access: Private, Static
470 // Description: Gets the color of the texel at byte p, given that the
471 // texture is in format F_blue.
472 ////////////////////////////////////////////////////////////////////
473 void TexturePeeker::
474 get_texel_b(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
475  color[0] = 0.0f;
476  color[1] = 0.0f;
477  color[2] = (*get_component)(p);
478  color[3] = 1.0f;
479 }
480 
481 ////////////////////////////////////////////////////////////////////
482 // Function: TexturePeeker::get_texel_a
483 // Access: Private, Static
484 // Description: Gets the color of the texel at byte p, given that the
485 // texture is in format F_alpha.
486 ////////////////////////////////////////////////////////////////////
487 void TexturePeeker::
488 get_texel_a(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
489  color[0] = 0.0f;
490  color[1] = 0.0f;
491  color[2] = 1.0f;
492  color[3] = (*get_component)(p);
493 }
494 
495 ////////////////////////////////////////////////////////////////////
496 // Function: TexturePeeker::get_texel_l
497 // Access: Private, Static
498 // Description: Gets the color of the texel at byte p, given that the
499 // texture is in format F_luminance.
500 ////////////////////////////////////////////////////////////////////
501 void TexturePeeker::
502 get_texel_l(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
503  color[0] = (*get_component)(p);
504  color[1] = color[0];
505  color[2] = color[0];
506  color[3] = 1.0f;
507 }
508 
509 ////////////////////////////////////////////////////////////////////
510 // Function: TexturePeeker::get_texel_la
511 // Access: Private, Static
512 // Description: Gets the color of the texel at byte p, given that the
513 // texture is in format F_luminance_alpha or similar.
514 ////////////////////////////////////////////////////////////////////
515 void TexturePeeker::
516 get_texel_la(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
517  color[0] = (*get_component)(p);
518  color[1] = color[0];
519  color[2] = color[0];
520  color[3] = (*get_component)(p);
521 }
522 
523 ////////////////////////////////////////////////////////////////////
524 // Function: TexturePeeker::get_texel_rgb
525 // Access: Private, Static
526 // Description: Gets the color of the texel at byte p, given that the
527 // texture is in format F_rgb or similar.
528 ////////////////////////////////////////////////////////////////////
529 void TexturePeeker::
530 get_texel_rgb(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
531  color[2] = (*get_component)(p);
532  color[1] = (*get_component)(p);
533  color[0] = (*get_component)(p);
534  color[3] = 1.0f;
535 }
536 
537 ////////////////////////////////////////////////////////////////////
538 // Function: TexturePeeker::get_texel_rgba
539 // Access: Private, Static
540 // Description: Gets the color of the texel at byte p, given that the
541 // texture is in format F_rgba or similar.
542 ////////////////////////////////////////////////////////////////////
543 void TexturePeeker::
544 get_texel_rgba(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
545  color[2] = (*get_component)(p);
546  color[1] = (*get_component)(p);
547  color[0] = (*get_component)(p);
548  color[3] = (*get_component)(p);
549 }
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:75
void lookup(LColor &color, PN_stdfloat u, PN_stdfloat v) const
Fills "color" with the RGBA color of the texel at point (u, v).
const Element * p() const
Function p() is similar to the function from ConstPointerTo.
void clear()
To empty the PTA, use the clear() method, since assignment to NULL is problematic (given the ambiguit...
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
void filter_rect(LColor &color, PN_stdfloat min_u, PN_stdfloat min_v, PN_stdfloat max_u, PN_stdfloat max_v) const
Fills "color" with the average RGBA color of the texels within the rectangle defined by the specified...
bool is_null() const
Returns true if the PointerTo is a NULL pointer, false otherwise.
Definition: pointerToVoid.I:54