Panda3D
texturePeeker.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 texturePeeker.cxx
10  * @author drose
11  * @date 2008-08-26
12  */
13 
14 #include "texturePeeker.h"
15 
16 
17 /**
18  * Use Texture::peek() to construct a TexturePeeker.
19  *
20  * This constructor is called only by Texture::peek(), and assumes the
21  * texture's lock is already held.
22  */
23 TexturePeeker::
24 TexturePeeker(Texture *tex, Texture::CData *cdata) {
25  if (cdata->_texture_type == Texture::TT_cube_map) {
26  // Cube map texture. We'll need to map from (u, v, w) to (u, v) within
27  // the appropriate page, where w indicates the page.
28 
29  // TODO: handle cube maps.
30  return;
31 
32  } else {
33  // Regular 1-d, 2-d, or 3-d texture. The coordinates map directly.
34  // Simple ram images are possible if it is a 2-d texture.
35  if (tex->do_has_ram_image(cdata) && cdata->_ram_image_compression == Texture::CM_off) {
36  // Get the regular RAM image if it is available.
37  _image = tex->do_get_ram_image(cdata);
38  _x_size = cdata->_x_size;
39  _y_size = cdata->_y_size;
40  _z_size = cdata->_z_size;
41  _component_width = cdata->_component_width;
42  _num_components = cdata->_num_components;
43  _format = cdata->_format;
44  _component_type = cdata->_component_type;
45 
46  } else if (!cdata->_simple_ram_image._image.empty()) {
47  // Get the simple RAM image if *that* is available.
48  _image = cdata->_simple_ram_image._image;
49  _x_size = cdata->_simple_x_size;
50  _y_size = cdata->_simple_y_size;
51  _z_size = 1;
52 
53  _component_width = 1;
54  _num_components = 4;
55  _format = Texture::F_rgba;
56  _component_type = Texture::T_unsigned_byte;
57 
58  } else {
59  // Failing that, reload and get the uncompressed RAM image.
60  _image = tex->do_get_uncompressed_ram_image(cdata);
61  _x_size = cdata->_x_size;
62  _y_size = cdata->_y_size;
63  _z_size = cdata->_z_size;
64  _component_width = cdata->_component_width;
65  _num_components = cdata->_num_components;
66  _format = cdata->_format;
67  _component_type = cdata->_component_type;
68  }
69  }
70 
71  if (_image.is_null()) {
72  return;
73  }
74  _pixel_width = _component_width * _num_components;
75 
76  switch (_component_type) {
77  case Texture::T_unsigned_byte:
78  _get_component = Texture::get_unsigned_byte;
79  break;
80 
81  case Texture::T_unsigned_short:
82  _get_component = Texture::get_unsigned_short;
83  break;
84 
85  case Texture::T_unsigned_int:
86  _get_component = Texture::get_unsigned_int;
87  break;
88 
89  case Texture::T_float:
90  _get_component = Texture::get_float;
91  break;
92 
93  case Texture::T_half_float:
94  _get_component = Texture::get_half_float;
95  break;
96 
97  case Texture::T_unsigned_int_24_8:
98  _get_component = Texture::get_unsigned_int_24;
99  break;
100 
101  default:
102  // Not supported.
103  _image.clear();
104  return;
105  }
106 
107  switch (_format) {
108  case Texture::F_depth_stencil:
109  case Texture::F_depth_component:
110  case Texture::F_depth_component16:
111  case Texture::F_depth_component24:
112  case Texture::F_depth_component32:
113  case Texture::F_red:
114  case Texture::F_r16:
115  case Texture::F_r32:
116  case Texture::F_r32i:
117  _get_texel = get_texel_r;
118  break;
119 
120  case Texture::F_green:
121  _get_texel = get_texel_g;
122  break;
123 
124  case Texture::F_blue:
125  _get_texel = get_texel_b;
126  break;
127 
128  case Texture::F_alpha:
129  _get_texel = get_texel_a;
130  break;
131 
132  case Texture::F_luminance:
133  case Texture::F_sluminance:
134  _get_texel = get_texel_l;
135  break;
136 
137  case Texture::F_luminance_alpha:
138  case Texture::F_sluminance_alpha:
139  case Texture::F_luminance_alphamask:
140  _get_texel = get_texel_la;
141  break;
142 
143  case Texture::F_rg16:
144  case Texture::F_rg32:
145  case Texture::F_rg:
146  _get_texel = get_texel_rg;
147  break;
148 
149  case Texture::F_rgb:
150  case Texture::F_rgb5:
151  case Texture::F_rgb8:
152  case Texture::F_rgb12:
153  case Texture::F_rgb16:
154  case Texture::F_rgb332:
155  case Texture::F_r11_g11_b10:
156  case Texture::F_rgb9_e5:
157  case Texture::F_rgb32:
158  _get_texel = get_texel_rgb;
159  break;
160 
161  case Texture::F_rgba:
162  case Texture::F_rgbm:
163  case Texture::F_rgba4:
164  case Texture::F_rgba5:
165  case Texture::F_rgba8:
166  case Texture::F_rgba12:
167  case Texture::F_rgba16:
168  case Texture::F_rgba32:
169  case Texture::F_rgb10_a2:
170  _get_texel = get_texel_rgba;
171  break;
172 
173  case Texture::F_srgb:
174  if (_component_type == Texture::T_unsigned_byte) {
175  _get_texel = get_texel_srgb;
176  } else {
177  gobj_cat.error()
178  << "sRGB texture should have component type T_unsigned_byte\n";
179  }
180  break;
181 
182  case Texture::F_srgb_alpha:
183  if (_component_type == Texture::T_unsigned_byte) {
184  _get_texel = get_texel_srgba;
185  } else {
186  gobj_cat.error()
187  << "sRGB texture should have component type T_unsigned_byte\n";
188  }
189  break;
190 
191  default:
192  // Not supported.
193  gobj_cat.error() << "Unsupported texture peeker format: "
194  << Texture::format_format(_format) << std::endl;
195  _image.clear();
196  return;
197  }
198 }
199 
200 
201 /**
202  * Fills "color" with the RGBA color of the texel at point (u, v).
203  *
204  * The texel color is determined via nearest-point sampling (no filtering of
205  * adjacent pixels), regardless of the filter type associated with the
206  * texture. u, v, and w will wrap around regardless of the texture's wrap
207  * mode.
208  */
210 lookup(LColor &color, PN_stdfloat u, PN_stdfloat v) const {
211  int x = int((u - cfloor(u)) * (PN_stdfloat)_x_size) % _x_size;
212  int y = int((v - cfloor(v)) * (PN_stdfloat)_y_size) % _y_size;
213  fetch_pixel(color, x, y);
214 }
215 
216 /**
217  * Works like TexturePeeker::lookup(), but instead uv-coordinates integer
218  * coordinates are used.
219  */
221 fetch_pixel(LColor& color, int x, int y) const {
222  nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size);
223  const unsigned char *p = _image.p() + (y * _x_size + x) * _pixel_width;
224  (*_get_texel)(color, p, _get_component);
225 }
226 
227 
228 /**
229  * Performs a bilinear lookup to retrieve the color value stored at the uv
230  * coordinate (u, v).
231  *
232  * In case the point is outside of the uv range, color is set to zero,
233  * and false is returned. Otherwise true is returned.
234  */
236 lookup_bilinear(LColor &color, PN_stdfloat u, PN_stdfloat v) const {
237  color = LColor::zero();
238 
239  u = u * _x_size - 0.5;
240  v = v * _y_size - 0.5;
241 
242  int min_u = int(floor(u));
243  int min_v = int(floor(v));
244 
245  PN_stdfloat frac_u = u - min_u;
246  PN_stdfloat frac_v = v - min_v;
247 
248  LColor p00(LColor::zero()), p01(LColor::zero()), p10(LColor::zero()), p11(LColor::zero());
249  PN_stdfloat w00 = 0.0, w01 = 0.0, w10 = 0.0, w11 = 0.0;
250 
251  if (has_pixel(min_u, min_v)) {
252  w00 = (1.0 - frac_v) * (1.0 - frac_u);
253  fetch_pixel(p00, min_u, min_v);
254  }
255  if (has_pixel(min_u + 1, min_v)) {
256  w10 = (1.0 - frac_v) * frac_u;
257  fetch_pixel(p10, min_u + 1, min_v);
258  }
259  if (has_pixel(min_u, min_v + 1)) {
260  w01 = frac_v * (1.0 - frac_u);
261  fetch_pixel(p01, min_u, min_v + 1);
262  }
263  if (has_pixel(min_u + 1, min_v + 1)) {
264  w11 = frac_v * frac_u;
265  fetch_pixel(p11, min_u + 1, min_v + 1);
266  }
267 
268  PN_stdfloat net_w = w00 + w01 + w10 + w11;
269  if (net_w == 0.0) {
270  return false;
271  }
272 
273  color = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11) / net_w;
274  return true;
275 }
276 
277 /**
278  * Fills "color" with the RGBA color of the texel at point (u, v, w).
279  *
280  * The texel color is determined via nearest-point sampling (no filtering of
281  * adjacent pixels), regardless of the filter type associated with the
282  * texture. u, v, and w will wrap around regardless of the texture's wrap
283  * mode.
284  */
286 lookup(LColor &color, PN_stdfloat u, PN_stdfloat v, PN_stdfloat w) const {
287  int x = int((u - cfloor(u)) * (PN_stdfloat)_x_size) % _x_size;
288  int y = int((v - cfloor(v)) * (PN_stdfloat)_y_size) % _y_size;
289  int z = int((w - cfloor(w)) * (PN_stdfloat)_z_size) % _z_size;
290 
291  nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size &&
292  z >= 0 && z < _z_size);
293  const unsigned char *p = _image.p() + (z * _x_size * _y_size + y * _x_size + x) * _pixel_width;
294 
295  (*_get_texel)(color, p, _get_component);
296 }
297 
298 /**
299  * Fills "color" with the average RGBA color of the texels within the
300  * rectangle defined by the specified coordinate range.
301  *
302  * The texel color is linearly filtered over the entire region. u, v, and w
303  * will wrap around regardless of the texture's wrap mode.
304  */
306 filter_rect(LColor &color,
307  PN_stdfloat min_u, PN_stdfloat min_v, PN_stdfloat max_u, PN_stdfloat max_v) const {
308  int min_x, max_x;
309  init_rect_minmax(min_x, max_x, min_u, max_u, _x_size);
310 
311  int min_y, max_y;
312  init_rect_minmax(min_y, max_y, min_v, max_v, _y_size);
313 
314  color.set(0.0f, 0.0f, 0.0f, 0.0f);
315  PN_stdfloat net = 0.0f;
316  accum_filter_y(color, net, 0,
317  min_x, max_x, min_u, max_u,
318  min_y, max_y, min_v, max_v,
319  1.0f);
320 
321  if (net != 0.0f) {
322  color /= net;
323  }
324 }
325 
326 /**
327  * Fills "color" with the average RGBA color of the texels within the
328  * rectangle defined by the specified coordinate range.
329  *
330  * The texel color is linearly filtered over the entire region. u, v, and w
331  * will wrap around regardless of the texture's wrap mode.
332  */
334 filter_rect(LColor &color,
335  PN_stdfloat min_u, PN_stdfloat min_v, PN_stdfloat min_w,
336  PN_stdfloat max_u, PN_stdfloat max_v, PN_stdfloat max_w) const {
337  int min_x, max_x;
338  init_rect_minmax(min_x, max_x, min_u, max_u, _x_size);
339 
340  int min_y, max_y;
341  init_rect_minmax(min_y, max_y, min_v, max_v, _y_size);
342 
343  int min_z, max_z;
344  init_rect_minmax(min_z, max_z, min_w, max_w, _z_size);
345 
346  color.set(0.0f, 0.0f, 0.0f, 0.0f);
347  PN_stdfloat net = 0.0f;
348  accum_filter_z(color, net,
349  min_x, max_x, min_u, max_u,
350  min_y, max_y, min_v, max_v,
351  min_z, max_z, min_w, max_w);
352 
353  if (net != 0.0f) {
354  color /= net;
355  }
356 }
357 
358 /**
359  * Sanity-checks min_u, max_u and computes min_x and min_y based on them.
360  * Also works for y and z.
361  */
362 void TexturePeeker::
363 init_rect_minmax(int &min_x, int &max_x, PN_stdfloat &min_u, PN_stdfloat &max_u,
364  int x_size) {
365  if (min_u > max_u) {
366  PN_stdfloat t = min_u;
367  min_u = max_u;
368  max_u = t;
369  }
370  if (max_u - min_u >= 1.0f) {
371  min_u = 0.0f;
372  max_u = 1.0f;
373  }
374  min_x = (int)cfloor(min_u * (PN_stdfloat)x_size);
375  max_x = (int)cceil(max_u * (PN_stdfloat)x_size);
376  nassertv(min_x <= max_x);
377 }
378 
379 /**
380  * Accumulates the range of pixels from min_z to max_z.
381  */
382 void TexturePeeker::
383 accum_filter_z(LColor &color, PN_stdfloat &net,
384  int min_x, int max_x, PN_stdfloat min_u, PN_stdfloat max_u,
385  int min_y, int max_y, PN_stdfloat min_v, PN_stdfloat max_v,
386  int min_z, int max_z, PN_stdfloat min_w, PN_stdfloat max_w) const {
387  nassertv(min_z >= 0 && min_z <= _z_size &&
388  max_z >= 0 && max_z <= _z_size);
389  int zi = min_z;
390 
391  if (min_z >= max_z - 1) {
392  // Within a single texel.
393  accum_filter_y(color, net, zi % _z_size,
394  min_x, max_x, min_u, max_u,
395  min_y, max_y, min_v, max_v,
396  1.0f);
397 
398  } else {
399  // First part-texel.
400  PN_stdfloat w = (min_z + 1) - min_w * _z_size;
401  accum_filter_y(color, net, zi % _z_size,
402  min_x, max_x, min_u, max_u,
403  min_y, max_y, min_v, max_v,
404  w);
405  int zs = max_z - 1;
406 
407  // Run of full texels.
408  zi = min_z + 1;
409  while (zi < zs) {
410  accum_filter_y(color, net, zi % _z_size,
411  min_x, max_x, min_u, max_u,
412  min_y, max_y, min_v, max_v,
413  1.0f);
414  ++zi;
415  }
416 
417  // Last part-texel.
418  w = max_w * _z_size - (max_z - 1);
419  accum_filter_y(color, net, zi % _z_size,
420  min_x, max_x, min_u, max_u,
421  min_y, max_y, min_v, max_v,
422  w);
423  }
424 }
425 
426 /**
427  * Accumulates the range of pixels from min_y to max_y.
428  */
429 void TexturePeeker::
430 accum_filter_y(LColor &color, PN_stdfloat &net, int zi,
431  int min_x, int max_x, PN_stdfloat min_u, PN_stdfloat max_u,
432  int min_y, int max_y, PN_stdfloat min_v, PN_stdfloat max_v,
433  PN_stdfloat weight) const {
434  nassertv(zi >= 0 && zi < _z_size);
435  nassertv(min_y >= 0 && min_y <= _y_size &&
436  max_y >= 0 && max_y <= _y_size);
437  int yi = min_y;
438 
439  if (min_y >= max_y - 1) {
440  // Within a single texel.
441  accum_filter_x(color, net, yi % _y_size, zi, min_x, max_x, min_u, max_u, weight);
442 
443  } else {
444  // First part-texel.
445  PN_stdfloat w = (min_y + 1) - min_v * _y_size;
446  accum_filter_x(color, net, yi % _y_size, zi, min_x, max_x, min_u, max_u, weight * w);
447  int ys = max_y - 1;
448 
449  // Run of full texels.
450  yi = min_y + 1;
451  while (yi < ys) {
452  accum_filter_x(color, net, yi % _y_size, zi, min_x, max_x, min_u, max_u, weight);
453  ++yi;
454  }
455 
456  // Last part-texel.
457  w = max_v * _y_size - (max_y - 1);
458  accum_filter_x(color, net, yi % _y_size, zi, min_x, max_x, min_u, max_u, weight * w);
459  }
460 }
461 
462 /**
463  * Accumulates the range of pixels from min_x to max_x.
464  */
465 void TexturePeeker::
466 accum_filter_x(LColor &color, PN_stdfloat &net, int yi, int zi,
467  int min_x, int max_x, PN_stdfloat min_u, PN_stdfloat max_u,
468  PN_stdfloat weight) const {
469  nassertv(yi >= 0 && yi < _y_size && zi >= 0 && zi < _z_size);
470  nassertv(min_x >= 0 && min_x <= _x_size &&
471  max_x >= 0 && max_x <= _x_size);
472 
473  // Compute the p corresponding to min_x.
474  int xi = min_x % _x_size;
475  const unsigned char *p = _image.p() + (zi * _x_size * _y_size + yi * _x_size + xi) * _pixel_width;
476 
477  if (min_x >= max_x - 1) {
478  // Within a single texel.
479  accum_texel(color, net, p, weight);
480 
481  } else {
482  // First part-texel.
483  PN_stdfloat w = (min_x + 1) - min_u * _x_size;
484  accum_texel(color, net, p, weight * w);
485  int xs = max_x - 1;
486 
487  // Run of full texels.
488  xi = min_x + 1;
489  while (xi < xs) {
490  if (xi == _x_size) {
491  xi = 0;
492  p = _image.p() + (zi * _x_size * _y_size + yi * _x_size + xi) * _pixel_width;
493  xs -= _x_size;
494  }
495  accum_texel(color, net, p, weight);
496  ++xi;
497  }
498 
499  // Last part-texel.
500  if (xi == _x_size) {
501  xi = 0;
502  p = _image.p() + (zi * _x_size * _y_size + yi * _x_size + xi) * _pixel_width;
503  }
504  w = max_u * _x_size - (max_x - 1);
505  accum_texel(color, net, p, weight * w);
506  }
507 }
508 
509 /**
510  * Accumulates a single texel into the total computed by filter_rect().
511  */
512 void TexturePeeker::
513 accum_texel(LColor &color, PN_stdfloat &net, const unsigned char *&p, PN_stdfloat weight) const {
514  LColor c;
515  (*_get_texel)(c, p, _get_component);
516  color += c * weight;
517  net += weight;
518 }
519 
520 /**
521  * Gets the color of the texel at byte p, given that the texture is in format
522  * F_red.
523  */
524 void TexturePeeker::
525 get_texel_r(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
526  color[0] = (*get_component)(p);
527  color[1] = 0.0f;
528  color[2] = 0.0f;
529  color[3] = 1.0f;
530 }
531 
532 /**
533  * Gets the color of the texel at byte p, given that the texture is in format
534  * F_green.
535  */
536 void TexturePeeker::
537 get_texel_g(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
538  color[0] = 0.0f;
539  color[1] = (*get_component)(p);
540  color[2] = 0.0f;
541  color[3] = 1.0f;
542 }
543 
544 /**
545  * Gets the color of the texel at byte p, given that the texture is in format
546  * F_blue.
547  */
548 void TexturePeeker::
549 get_texel_b(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
550  color[0] = 0.0f;
551  color[1] = 0.0f;
552  color[2] = (*get_component)(p);
553  color[3] = 1.0f;
554 }
555 
556 /**
557  * Gets the color of the texel at byte p, given that the texture is in format
558  * F_alpha.
559  */
560 void TexturePeeker::
561 get_texel_a(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
562  color[0] = 0.0f;
563  color[1] = 0.0f;
564  color[2] = 1.0f;
565  color[3] = (*get_component)(p);
566 }
567 
568 /**
569  * Gets the color of the texel at byte p, given that the texture is in format
570  * F_luminance.
571  */
572 void TexturePeeker::
573 get_texel_l(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
574  color[0] = (*get_component)(p);
575  color[1] = color[0];
576  color[2] = color[0];
577  color[3] = 1.0f;
578 }
579 
580 /**
581  * Gets the color of the texel at byte p, given that the texture is in format
582  * F_luminance_alpha or similar.
583  */
584 void TexturePeeker::
585 get_texel_la(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
586  color[0] = (*get_component)(p);
587  color[1] = color[0];
588  color[2] = color[0];
589  color[3] = (*get_component)(p);
590 }
591 
592 /**
593  * Gets the color of the texel at byte p, given that the texture is in format
594  * F_rg or similar.
595  */
596 void TexturePeeker::
597 get_texel_rg(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
598  color[0] = (*get_component)(p);
599  color[1] = (*get_component)(p);
600  color[2] = 0.0f;
601  color[3] = 1.0f;
602 }
603 
604 /**
605  * Gets the color of the texel at byte p, given that the texture is in format
606  * F_rgb or similar.
607  */
608 void TexturePeeker::
609 get_texel_rgb(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
610  color[2] = (*get_component)(p);
611  color[1] = (*get_component)(p);
612  color[0] = (*get_component)(p);
613  color[3] = 1.0f;
614 }
615 
616 /**
617  * Gets the color of the texel at byte p, given that the texture is in format
618  * F_rgba or similar.
619  */
620 void TexturePeeker::
621 get_texel_rgba(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
622  color[2] = (*get_component)(p);
623  color[1] = (*get_component)(p);
624  color[0] = (*get_component)(p);
625  color[3] = (*get_component)(p);
626 }
627 
628 /**
629  * Gets the color of the texel at byte p, given that the texture is in format
630  * F_srgb or similar.
631  */
632 void TexturePeeker::
633 get_texel_srgb(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
634  color[2] = decode_sRGB_float(*p++);
635  color[1] = decode_sRGB_float(*p++);
636  color[0] = decode_sRGB_float(*p++);
637  color[3] = 1.0f;
638 }
639 
640 /**
641  * Gets the color of the texel at byte p, given that the texture is in format
642  * F_srgb_alpha or similar.
643  */
644 void TexturePeeker::
645 get_texel_srgba(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
646  color[2] = decode_sRGB_float(*p++);
647  color[1] = decode_sRGB_float(*p++);
648  color[0] = decode_sRGB_float(*p++);
649  color[3] = (*get_component)(p);
650 }
Texture::format_format
static std::string format_format(Format f)
Returns the indicated Format converted to a string word.
Definition: texture.cxx:2163
decode_sRGB_float
BEGIN_PUBLISH EXPCL_PANDA_PNMIMAGE float decode_sRGB_float(unsigned char val)
Decodes the sRGB-encoded unsigned char value to a linearized float in the range 0-1.
Definition: convert_srgb.I:18
TexturePeeker::lookup_bilinear
bool lookup_bilinear(LColor &color, PN_stdfloat u, PN_stdfloat v) const
Performs a bilinear lookup to retrieve the color value stored at the uv coordinate (u,...
Definition: texturePeeker.cxx:236
TexturePeeker::filter_rect
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...
Definition: texturePeeker.cxx:306
Texture
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
ConstPointerToArray::clear
void clear()
To empty the PTA, use the clear() method, since assignment to NULL is problematic (given the ambiguit...
Definition: pointerToArray.I:1109
TexturePeeker::lookup
void lookup(LColor &color, PN_stdfloat u, PN_stdfloat v) const
Fills "color" with the RGBA color of the texel at point (u, v).
Definition: texturePeeker.cxx:210
TexturePeeker::fetch_pixel
void fetch_pixel(LColor &color, int x, int y) const
Works like TexturePeeker::lookup(), but instead uv-coordinates integer coordinates are used.
Definition: texturePeeker.cxx:221
texturePeeker.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ConstPointerToArray::p
const Element * p() const
Function p() is similar to the function from ConstPointerTo.
Definition: pointerToArray.I:893
TexturePeeker::has_pixel
bool has_pixel(int x, int y) const
Returns whether a given coordinate is inside of the texture dimensions.
Definition: texturePeeker.I:56