Panda3D
pfmFile.cxx
1 // Filename: pfmFile.cxx
2 // Created by: drose (23Dec10)
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 "config_pnmimage.h"
16 #include "pfmFile.h"
17 #include "virtualFileSystem.h"
18 #include "pandaFileStream.h"
19 #include "littleEndian.h"
20 #include "bigEndian.h"
21 #include "cmath.h"
22 #include "pnmImage.h"
23 #include "pnmReader.h"
24 #include "pnmWriter.h"
25 #include "string_utils.h"
26 #include "look_at.h"
27 
28 ////////////////////////////////////////////////////////////////////
29 // Function: PfmFile::Constructor
30 // Access: Published
31 // Description:
32 ////////////////////////////////////////////////////////////////////
33 PfmFile::
34 PfmFile() {
35  _has_no_data_value = false;
36  _has_no_data_threshold = false;
37  _no_data_value = LPoint4f::zero();
38  _has_point = has_point_noop;
39  clear();
40 }
41 
42 ////////////////////////////////////////////////////////////////////
43 // Function: PfmFile::Copy Constructor
44 // Access: Published
45 // Description:
46 ////////////////////////////////////////////////////////////////////
47 PfmFile::
48 PfmFile(const PfmFile &copy) :
49  PNMImageHeader(copy),
50  _table(copy._table),
51  _scale(copy._scale),
52  _has_no_data_value(copy._has_no_data_value),
53  _has_no_data_threshold(copy._has_no_data_threshold),
54  _no_data_value(copy._no_data_value),
55  _has_point(copy._has_point)
56 {
57 }
58 
59 ////////////////////////////////////////////////////////////////////
60 // Function: PfmFile::Copy Assignment
61 // Access: Published
62 // Description:
63 ////////////////////////////////////////////////////////////////////
64 void PfmFile::
65 operator = (const PfmFile &copy) {
66  PNMImageHeader::operator = (copy);
67  _table = copy._table;
68  _scale = copy._scale;
69  _has_no_data_value = copy._has_no_data_value;
70  _has_no_data_threshold = copy._has_no_data_threshold;
71  _no_data_value = copy._no_data_value;
72  _has_point = copy._has_point;
73 }
74 
75 ////////////////////////////////////////////////////////////////////
76 // Function: PfmFile::clear
77 // Access: Published
78 // Description: Eliminates all data in the file.
79 ////////////////////////////////////////////////////////////////////
80 void PfmFile::
81 clear() {
82  _x_size = 0;
83  _y_size = 0;
84  _scale = 1.0;
85  _num_channels = 0;
86  _table.clear();
88 }
89 
90 ////////////////////////////////////////////////////////////////////
91 // Function: PfmFile::clear
92 // Access: Published
93 // Description: Resets to an empty table with a specific size. The
94 // case of num_channels == 0 is allowed only in the case
95 // that x_size and y_size are also == 0; and this makes
96 // an empty (and invalid) PfmFile.
97 ////////////////////////////////////////////////////////////////////
98 void PfmFile::
99 clear(int x_size, int y_size, int num_channels) {
100  nassertv(x_size >= 0 && y_size >= 0);
101  nassertv(num_channels > 0 && num_channels <= 4 || (x_size == 0 && y_size == 0 && num_channels == 0));
102 
103  _x_size = x_size;
104  _y_size = y_size;
105  _scale = 1.0;
106  _num_channels = num_channels;
107 
108  _table.clear();
109  int size = _x_size * _y_size * _num_channels;
110 
111  // We allocate a little bit bigger to allow safe overflow: you can
112  // call get_point3() or get_point4() on the last point of a 1- or
113  // 3-channel image.
114  _table.insert(_table.end(), size + 4, (PN_float32)0.0);
115 
117 }
118 
119 ////////////////////////////////////////////////////////////////////
120 // Function: PfmFile::read
121 // Access: Published
122 // Description: Reads the PFM data from the indicated file, returning
123 // true on success, false on failure.
124 //
125 // This can also handle reading a standard image file
126 // supported by PNMImage; it will be quietly converted
127 // to a floating-point type.
128 ////////////////////////////////////////////////////////////////////
129 bool PfmFile::
130 read(const Filename &fullpath) {
132 
133  Filename filename = Filename::binary_filename(fullpath);
134  PT(VirtualFile) file = vfs->get_file(filename);
135  if (file == (VirtualFile *)NULL) {
136  // No such file.
137  pnmimage_cat.error()
138  << "Could not find " << fullpath << "\n";
139  return false;
140  }
141 
142  if (pnmimage_cat.is_debug()) {
143  pnmimage_cat.debug()
144  << "Reading PFM file " << filename << "\n";
145  }
146 
147  istream *in = file->open_read_file(true);
148  bool success = read(*in, fullpath);
149  vfs->close_read_file(in);
150 
151  return success;
152 }
153 
154 ////////////////////////////////////////////////////////////////////
155 // Function: PfmFile::read
156 // Access: Published
157 // Description: Reads the PFM data from the indicated stream,
158 // returning true on success, false on failure.
159 //
160 // This can also handle reading a standard image file
161 // supported by PNMImage; it will be quietly converted
162 // to a floating-point type.
163 ////////////////////////////////////////////////////////////////////
164 bool PfmFile::
165 read(istream &in, const Filename &fullpath) {
166  PNMReader *reader = make_reader(&in, false, fullpath);
167  if (reader == (PNMReader *)NULL) {
168  clear();
169  return false;
170  }
171  return read(reader);
172 }
173 
174 ////////////////////////////////////////////////////////////////////
175 // Function: PfmFile::read
176 // Access: Published
177 // Description: Reads the PFM data using the indicated PNMReader.
178 //
179 // The PNMReader is always deleted upon completion,
180 // whether successful or not.
181 ////////////////////////////////////////////////////////////////////
182 bool PfmFile::
183 read(PNMReader *reader) {
184  clear();
185 
186  if (reader == NULL) {
187  return false;
188  }
189 
190  if (!reader->is_valid()) {
191  delete reader;
192  return false;
193  }
194 
195  if (!reader->is_floating_point()) {
196  // Not a floating-point file. Quietly convert it.
197  PNMImage pnm;
198  if (!pnm.read(reader)) {
199  return false;
200  }
201 
202  return load(pnm);
203  }
204 
205  bool success = reader->read_pfm(*this);
206  delete reader;
207  return success;
208 }
209 
210 ////////////////////////////////////////////////////////////////////
211 // Function: PfmFile::write
212 // Access: Published
213 // Description: Writes the PFM data to the indicated file, returning
214 // true on success, false on failure.
215 //
216 // If the type implied by the filename extension
217 // supports floating-point, the data will be written
218 // directly; otherwise, the floating-point data will be
219 // quietly converted to the appropriate integer type.
220 ////////////////////////////////////////////////////////////////////
221 bool PfmFile::
222 write(const Filename &fullpath) {
223  if (!is_valid()) {
224  pnmimage_cat.error()
225  << "PFM file is invalid.\n";
226  return false;
227  }
228 
229  Filename filename = Filename::binary_filename(fullpath);
230  pofstream out;
231  if (!filename.open_write(out)) {
232  pnmimage_cat.error()
233  << "Unable to open " << filename << "\n";
234  return false;
235  }
236 
237  if (pnmimage_cat.is_debug()) {
238  pnmimage_cat.debug()
239  << "Writing PFM file " << filename << "\n";
240  }
241 
242  return write(out, fullpath);
243 }
244 
245 ////////////////////////////////////////////////////////////////////
246 // Function: PfmFile::write
247 // Access: Published
248 // Description: Writes the PFM data to the indicated stream,
249 // returning true on success, false on failure.
250 ////////////////////////////////////////////////////////////////////
251 bool PfmFile::
252 write(ostream &out, const Filename &fullpath) {
253  if (!is_valid()) {
254  return false;
255  }
256 
257  PNMWriter *writer = make_writer(&out, false, fullpath);
258  if (writer == (PNMWriter *)NULL) {
259  return false;
260  }
261 
262  return write(writer);
263 }
264 
265 ////////////////////////////////////////////////////////////////////
266 // Function: PfmFile::write
267 // Access: Published
268 // Description: Writes the PFM data using the indicated PNMWriter.
269 //
270 // The PNMWriter is always deleted upon completion,
271 // whether successful or not.
272 ////////////////////////////////////////////////////////////////////
273 bool PfmFile::
274 write(PNMWriter *writer) {
275  if (writer == NULL) {
276  return false;
277  }
278 
279  if (!is_valid()) {
280  delete writer;
281  return false;
282  }
283 
284  writer->copy_header_from(*this);
285 
286  if (!writer->supports_floating_point()) {
287  // Hmm, it's an integer file type. Convert it from the
288  // floating-point data we have.
289  PNMImage pnmimage;
290  if (!store(pnmimage)) {
291  delete writer;
292  return false;
293  }
294  writer->copy_header_from(pnmimage);
295  bool success = (writer->write_data(pnmimage.get_array(), pnmimage.get_alpha_array()) != 0);
296  delete writer;
297  return success;
298  }
299 
300  bool success = writer->write_pfm(*this);
301  delete writer;
302  return success;
303 }
304 
305 ////////////////////////////////////////////////////////////////////
306 // Function: PfmFile::load
307 // Access: Published
308 // Description: Fills the PfmFile with the data from the indicated
309 // PNMImage, converted to floating-point values.
310 ////////////////////////////////////////////////////////////////////
311 bool PfmFile::
312 load(const PNMImage &pnmimage) {
313  if (!pnmimage.is_valid()) {
314  clear();
315  return false;
316  }
317 
318  int num_channels = pnmimage.get_num_channels();
319 
320  clear(pnmimage.get_x_size(), pnmimage.get_y_size(), num_channels);
321  switch (num_channels) {
322  case 1:
323  {
324  for (int yi = 0; yi < pnmimage.get_y_size(); ++yi) {
325  for (int xi = 0; xi < pnmimage.get_x_size(); ++xi) {
326  _table[yi * _x_size + xi] = pnmimage.get_gray(xi, yi);
327  }
328  }
329  }
330  break;
331 
332  case 2:
333  {
334  for (int yi = 0; yi < pnmimage.get_y_size(); ++yi) {
335  for (int xi = 0; xi < pnmimage.get_x_size(); ++xi) {
336  PN_float32 *point = &_table[(yi * _x_size + xi) * _num_channels];
337  point[0] = pnmimage.get_gray(xi, yi);
338  point[1] = pnmimage.get_alpha(xi, yi);
339  }
340  }
341  }
342  break;
343 
344  case 3:
345  {
346  for (int yi = 0; yi < pnmimage.get_y_size(); ++yi) {
347  for (int xi = 0; xi < pnmimage.get_x_size(); ++xi) {
348  PN_float32 *point = &_table[(yi * _x_size + xi) * _num_channels];
349  LRGBColorf xel = pnmimage.get_xel(xi, yi);
350  point[0] = xel[0];
351  point[1] = xel[1];
352  point[2] = xel[2];
353  }
354  }
355  }
356  break;
357 
358  case 4:
359  {
360  for (int yi = 0; yi < pnmimage.get_y_size(); ++yi) {
361  for (int xi = 0; xi < pnmimage.get_x_size(); ++xi) {
362  PN_float32 *point = &_table[(yi * _x_size + xi) * _num_channels];
363  LColorf xel = pnmimage.get_xel_a(xi, yi);
364  point[0] = xel[0];
365  point[1] = xel[1];
366  point[2] = xel[2];
367  point[3] = xel[3];
368  }
369  }
370  }
371  break;
372 
373  default:
374  nassertr(false, false);
375  }
376  return true;
377 }
378 
379 
380 ////////////////////////////////////////////////////////////////////
381 // Function: PfmFile::store
382 // Access: Published
383 // Description: Copies the data to the indicated PNMImage, converting
384 // to RGB values.
385 ////////////////////////////////////////////////////////////////////
386 bool PfmFile::
387 store(PNMImage &pnmimage) const {
388  if (!is_valid()) {
389  pnmimage.clear();
390  return false;
391  }
392 
393  int num_channels = get_num_channels();
394  pnmimage.clear(get_x_size(), get_y_size(), num_channels, PGM_MAXMAXVAL);
395  switch (num_channels) {
396  case 1:
397  {
398  for (int yi = 0; yi < get_y_size(); ++yi) {
399  for (int xi = 0; xi < get_x_size(); ++xi) {
400  pnmimage.set_gray(xi, yi, _table[yi * _x_size + xi]);
401  }
402  }
403  }
404  break;
405 
406  case 2:
407  {
408  for (int yi = 0; yi < get_y_size(); ++yi) {
409  for (int xi = 0; xi < get_x_size(); ++xi) {
410  const LPoint2f &point = get_point2(xi, yi);
411  pnmimage.set_gray(xi, yi, point[0]);
412  pnmimage.set_alpha(xi, yi, point[1]);
413  }
414  }
415  }
416  break;
417 
418  case 3:
419  {
420  for (int yi = 0; yi < get_y_size(); ++yi) {
421  for (int xi = 0; xi < get_x_size(); ++xi) {
422  const LPoint3f &point = get_point3(xi, yi);
423  pnmimage.set_xel(xi, yi, point[0], point[1], point[2]);
424  }
425  }
426  }
427  break;
428 
429  case 4:
430  {
431  for (int yi = 0; yi < get_y_size(); ++yi) {
432  for (int xi = 0; xi < get_x_size(); ++xi) {
433  const LPoint4f &point = get_point4(xi, yi);
434  pnmimage.set_xel_a(xi, yi, point[0], point[1], point[2], point[3]);
435  }
436  }
437  }
438  break;
439 
440  default:
441  nassertr(false, false);
442  }
443  return true;
444 }
445 
446 ////////////////////////////////////////////////////////////////////
447 // Function: PfmFile::store_mask
448 // Access: Published
449 // Description: Stores 1 or 0 values into the indicated PNMImage,
450 // according to has_point() for each pixel. Each valid
451 // point gets a 1 value; each nonexistent point gets a 0
452 // value.
453 ////////////////////////////////////////////////////////////////////
454 bool PfmFile::
455 store_mask(PNMImage &pnmimage) const {
456  if (!is_valid()) {
457  pnmimage.clear();
458  return false;
459  }
460 
461  pnmimage.clear(get_x_size(), get_y_size(), 1, 255);
462  for (int yi = 0; yi < get_y_size(); ++yi) {
463  for (int xi = 0; xi < get_x_size(); ++xi) {
464  pnmimage.set_gray(xi, yi, has_point(xi, yi));
465  }
466  }
467  return true;
468 }
469 
470 ////////////////////////////////////////////////////////////////////
471 // Function: PfmFile::fill
472 // Access: Published
473 // Description: Fills the table with all of the same value.
474 ////////////////////////////////////////////////////////////////////
475 void PfmFile::
476 fill(const LPoint4f &value) {
477  switch (_num_channels) {
478  case 1:
479  {
480  for (int yi = 0; yi < _y_size; ++yi) {
481  for (int xi = 0; xi < _x_size; ++xi) {
482  _table[(yi * _x_size + xi)] = value[0];
483  }
484  }
485  }
486  break;
487 
488  case 2:
489  {
490  for (int yi = 0; yi < _y_size; ++yi) {
491  for (int xi = 0; xi < _x_size; ++xi) {
492  (*(LPoint2f *)&_table[(yi * _x_size + xi) * _num_channels]).set(value[0], value[1]);
493  }
494  }
495  }
496  break;
497 
498  case 3:
499  {
500  for (int yi = 0; yi < _y_size; ++yi) {
501  for (int xi = 0; xi < _x_size; ++xi) {
502  (*(LPoint3f *)&_table[(yi * _x_size + xi) * _num_channels]).set(value[0], value[1], value[2]);
503  }
504  }
505  }
506  break;
507 
508  case 4:
509  {
510  for (int yi = 0; yi < _y_size; ++yi) {
511  for (int xi = 0; xi < _x_size; ++xi) {
512  *(LPoint4f *)&_table[(yi * _x_size + xi) * _num_channels] = value;
513  }
514  }
515  }
516  break;
517  }
518 }
519 
520 ////////////////////////////////////////////////////////////////////
521 // Function: PfmFile::fill_nan
522 // Access: Published
523 // Description: Fills the table with all NaN.
524 ////////////////////////////////////////////////////////////////////
525 void PfmFile::
527  PN_float32 nan = make_nan((PN_float32)0.0);
528  LPoint4f nan4(nan, nan, nan, nan);
529  fill(nan4);
530 }
531 
532 ////////////////////////////////////////////////////////////////////
533 // Function: PfmFile::fill_no_data_value
534 // Access: Published
535 // Description: Fills the table with the current no_data value, so
536 // that the table is empty.
537 ////////////////////////////////////////////////////////////////////
538 void PfmFile::
540  fill(_no_data_value);
541 }
542 
543 ////////////////////////////////////////////////////////////////////
544 // Function: PfmFile::fill_channel
545 // Access: Published
546 // Description: Fills the indicated channel with all of the same
547 // value, leaving the other channels unchanged.
548 ////////////////////////////////////////////////////////////////////
549 void PfmFile::
550 fill_channel(int channel, PN_float32 value) {
551  nassertv(channel >= 0 && channel < _num_channels);
552 
553  for (int yi = 0; yi < _y_size; ++yi) {
554  for (int xi = 0; xi < _x_size; ++xi) {
555  _table[(yi * _x_size + xi) * _num_channels + channel] = value;
556  }
557  }
558 }
559 
560 ////////////////////////////////////////////////////////////////////
561 // Function: PfmFile::fill_channel_nan
562 // Access: Published
563 // Description: Fills the indicated channel with NaN, leaving the
564 // other channels unchanged.
565 ////////////////////////////////////////////////////////////////////
566 void PfmFile::
567 fill_channel_nan(int channel) {
568  PN_float32 nan = make_nan((PN_float32)0.0);
569  fill_channel(channel, nan);
570 }
571 
572 ////////////////////////////////////////////////////////////////////
573 // Function: PfmFile::fill_channel_masked
574 // Access: Published
575 // Description: Fills the indicated channel with all of the same
576 // value, but only where the table already has a data
577 // point. Leaves empty points unchanged.
578 ////////////////////////////////////////////////////////////////////
579 void PfmFile::
580 fill_channel_masked(int channel, PN_float32 value) {
581  nassertv(channel >= 0 && channel < _num_channels);
582 
583  if (!_has_no_data_value) {
584  fill_channel(channel, value);
585  } else {
586  for (int yi = 0; yi < _y_size; ++yi) {
587  for (int xi = 0; xi < _x_size; ++xi) {
588  if (has_point(xi, yi)) {
589  _table[(yi * _x_size + xi) * _num_channels + channel] = value;
590  }
591  }
592  }
593  }
594 }
595 
596 ////////////////////////////////////////////////////////////////////
597 // Function: PfmFile::fill_channel_masked_nan
598 // Access: Published
599 // Description: Fills the indicated channel with NaN, but only where
600 // the table already has a data point. Leaves empty
601 // points unchanged.
602 ////////////////////////////////////////////////////////////////////
603 void PfmFile::
605  PN_float32 nan = make_nan((PN_float32)0.0);
606  fill_channel_masked(channel, nan);
607 }
608 
609 ////////////////////////////////////////////////////////////////////
610 // Function: PfmFile::calc_average_point
611 // Access: Published
612 // Description: Computes the unweighted average point of all points
613 // within the box centered at (x, y) with the indicated
614 // Manhattan-distance radius. Missing points are
615 // assigned the value of their nearest neighbor.
616 // Returns true if successful, or false if the point
617 // value cannot be determined.
618 ////////////////////////////////////////////////////////////////////
619 bool PfmFile::
620 calc_average_point(LPoint3f &result, PN_float32 x, PN_float32 y, PN_float32 radius) const {
621  result = LPoint3f::zero();
622 
623  int min_x = int(ceil(x - radius));
624  int min_y = int(ceil(y - radius));
625  int max_x = int(floor(x + radius));
626  int max_y = int(floor(y + radius));
627 
628  // We first construct a mini-grid of x_size by y_size integer values
629  // to index into the main table. This indirection allows us to fill
630  // in the holes in the mini-grid with the nearest known values
631  // before we compute the average.
632  int x_size = max_x - min_x + 1;
633  int y_size = max_y - min_y + 1;
634  int size = x_size * y_size;
635  if (size == 0) {
636  return false;
637  }
638 
639  pvector<MiniGridCell> mini_grid;
640  mini_grid.insert(mini_grid.end(), size, MiniGridCell());
641 
642  // Now collect the known data points and apply them to the
643  // mini-grid.
644  min_x = max(min_x, 0);
645  min_y = max(min_y, 0);
646  max_x = min(max_x, _x_size - 1);
647  max_y = min(max_y, _y_size - 1);
648 
649  bool got_any = false;
650  int xi, yi;
651  for (yi = min_y; yi <= max_y; ++yi) {
652  for (xi = min_x; xi <= max_x; ++xi) {
653  if (!has_point(xi, yi)) {
654  continue;
655  }
656 
657  const LPoint3f &p = get_point(xi, yi);
658  int gi = (yi - min_y) * y_size + (xi - min_x);
659  nassertr(gi >= 0 && gi < size, false);
660  mini_grid[gi]._sxi = xi;
661  mini_grid[gi]._syi = yi;
662  mini_grid[gi]._dist = 0;
663  got_any = true;
664  }
665  }
666 
667  if (!got_any) {
668  return false;
669  }
670 
671  // Now recursively fill in any missing holes in the mini-grid.
672  for (yi = 0; yi < y_size; ++yi) {
673  for (xi = 0; xi < x_size; ++xi) {
674  int gi = yi * x_size + xi;
675  if (mini_grid[gi]._dist == 0) {
676  int sxi = mini_grid[gi]._sxi;
677  int syi = mini_grid[gi]._syi;
678  fill_mini_grid(&mini_grid[0], x_size, y_size, xi + 1, yi, 1, sxi, syi);
679  fill_mini_grid(&mini_grid[0], x_size, y_size, xi - 1, yi, 1, sxi, syi);
680  fill_mini_grid(&mini_grid[0], x_size, y_size, xi, yi + 1, 1, sxi, syi);
681  fill_mini_grid(&mini_grid[0], x_size, y_size, xi, yi - 1, 1, sxi, syi);
682  }
683  }
684  }
685 
686  // Now the mini-grid is completely filled, so we can compute the
687  // average.
688  for (int gi = 0; gi < size; ++gi) {
689  int sxi = mini_grid[gi]._sxi;
690  int syi = mini_grid[gi]._syi;
691  const LPoint3f &p = get_point(sxi, syi);
692  result += p;
693  }
694 
695  result /= PN_float32(size);
696  return true;
697 }
698 
699 ////////////////////////////////////////////////////////////////////
700 // Function: PfmFile::calc_bilinear_point
701 // Access: Published
702 // Description: Computes the weighted average of the four nearest
703 // points to the floating-point index (x, y). Returns
704 // true if the point has any contributors, false if the
705 // point is unknown.
706 ////////////////////////////////////////////////////////////////////
707 bool PfmFile::
708 calc_bilinear_point(LPoint3f &result, PN_float32 x, PN_float32 y) const {
709  result = LPoint3f::zero();
710 
711  x = (x * _x_size - 0.5);
712  y = (y * _y_size - 0.5);
713 
714  int min_x = int(floor(x));
715  int min_y = int(floor(y));
716 
717  PN_float32 frac_x = x - min_x;
718  PN_float32 frac_y = y - min_y;
719 
720  LPoint3f p00(LPoint3f::zero()), p01(LPoint3f::zero()), p10(LPoint3f::zero()), p11(LPoint3f::zero());
721  PN_float32 w00 = 0.0, w01 = 0.0, w10 = 0.0, w11 = 0.0;
722 
723  if (has_point(min_x, min_y)) {
724  w00 = (1.0 - frac_y) * (1.0 - frac_x);
725  p00 = get_point(min_x, min_y);
726  }
727  if (has_point(min_x + 1, min_y)) {
728  w10 = (1.0 - frac_y) * frac_x;
729  p10 = get_point(min_x + 1, min_y);
730  }
731  if (has_point(min_x, min_y + 1)) {
732  w01 = frac_y * (1.0 - frac_x);
733  p01 = get_point(min_x, min_y + 1);
734  }
735  if (has_point(min_x + 1, min_y + 1)) {
736  w11 = frac_y * frac_x;
737  p11 = get_point(min_x + 1, min_y + 1);
738  }
739 
740  PN_float32 net_w = w00 + w01 + w10 + w11;
741  if (net_w == 0.0) {
742  return false;
743  }
744 
745  result = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11) / net_w;
746  return true;
747 }
748 
749 ////////////////////////////////////////////////////////////////////
750 // Function: PfmFile::calc_min_max
751 // Access: Published
752 // Description: Calculates the minimum and maximum x, y, and z depth
753 // component values, representing the bounding box of
754 // depth values, and places them in the indicated
755 // vectors. Returns true if successful, false if the
756 // mesh contains no points.
757 ////////////////////////////////////////////////////////////////////
758 bool PfmFile::
759 calc_min_max(LVecBase3f &min_depth, LVecBase3f &max_depth) const {
760  bool any_points = false;
761 
762  min_depth = LVecBase3f::zero();
763  max_depth = LVecBase3f::zero();
764 
765  for (int yi = 0; yi < _y_size; ++yi) {
766  for (int xi = 0; xi < _x_size; ++xi) {
767  if (!has_point(xi, yi)) {
768  continue;
769  }
770 
771  const LPoint3f &p = get_point(xi, yi);
772  if (!any_points) {
773  min_depth = p;
774  max_depth = p;
775  any_points = true;
776  } else {
777  min_depth[0] = min(min_depth[0], p[0]);
778  min_depth[1] = min(min_depth[1], p[1]);
779  min_depth[2] = min(min_depth[2], p[2]);
780  max_depth[0] = max(max_depth[0], p[0]);
781  max_depth[1] = max(max_depth[1], p[1]);
782  max_depth[2] = max(max_depth[2], p[2]);
783  }
784  }
785  }
786 
787  return any_points;
788 }
789 
790 ////////////////////////////////////////////////////////////////////
791 // Function: PfmFile::calc_autocrop
792 // Access: Published
793 // Description: Computes the minimum range of x and y across the PFM
794 // file that include all points. If there are no points
795 // with no_data_value in the grid--that is, all points
796 // are included--then this will return (0, get_x_size(),
797 // 0, get_y_size()).
798 ////////////////////////////////////////////////////////////////////
799 bool PfmFile::
800 calc_autocrop(int &x_begin, int &x_end, int &y_begin, int &y_end) const {
801  y_begin = 0;
802  while (is_row_empty(y_begin, 0, _x_size)) {
803  ++y_begin;
804  if (y_begin >= _y_size) {
805  // We've reached the end; the entire grid is empty.
806  x_begin = x_end = y_begin = y_end = 0;
807  return false;
808  }
809  }
810 
811  y_end = _y_size;
812  while (is_row_empty(y_end - 1, 0, _x_size)) {
813  --y_end;
814  nassertr(y_end > y_begin, false);
815  }
816 
817  // Now we've got the top and bottom bounds.
818  x_begin = 0;
819  while (is_column_empty(x_begin, y_begin, y_end)) {
820  ++x_begin;
821  nassertr(x_begin < _x_size, false);
822  }
823 
824  x_end = _x_size;
825  while (is_column_empty(x_end - 1, y_begin, y_end)) {
826  --x_end;
827  nassertr(x_end > x_begin, false);
828  }
829 
830  return true;
831 }
832 
833 ////////////////////////////////////////////////////////////////////
834 // Function: PfmFile::is_row_empty
835 // Access: Published
836 // Description: Returns true if all of the points on row y, in the range
837 // [x_begin, x_end), are the no_data value, or false if
838 // any one of these points has a value.
839 ////////////////////////////////////////////////////////////////////
840 bool PfmFile::
841 is_row_empty(int y, int x_begin, int x_end) const {
842  nassertr(y >= 0 && y < _y_size &&
843  x_begin >= 0 && x_begin <= x_end && x_end <= _x_size, false);
844 
845  if (!_has_no_data_value) {
846  return false;
847  }
848  for (int x = x_begin; x < x_end; ++x) {
849  if (has_point(x, y)) {
850  return false;
851  }
852  }
853 
854  return true;
855 }
856 
857 ////////////////////////////////////////////////////////////////////
858 // Function: PfmFile::is_column_empty
859 // Access: Published
860 // Description: Returns true if all of the points on column x, from
861 // [y_begin, y_end), are the no_data value, or false if
862 // any one of these points has a value.
863 ////////////////////////////////////////////////////////////////////
864 bool PfmFile::
865 is_column_empty(int x, int y_begin, int y_end) const {
866  nassertr(x >= 0 && x < _x_size &&
867  y_begin >= 0 && y_begin <= y_end && y_end <= _y_size, false);
868 
869  if (!_has_no_data_value) {
870  return false;
871  }
872  for (int y = y_begin; y < y_end; ++y) {
873  if (has_point(x, y)) {
874  return false;
875  }
876  }
877 
878  return true;
879 }
880 
881 ////////////////////////////////////////////////////////////////////
882 // Function: PfmFile::set_no_data_nan
883 // Access: Published
884 // Description: Sets the no_data_nan flag. When num_channels is
885 // nonzero, then a NaN value in any of the first
886 // num_channels channels indicates no data for that
887 // point. If num_channels is zero, then all points are
888 // valid.
889 //
890 // This is a special case of set_no_data_value().
891 ////////////////////////////////////////////////////////////////////
892 void PfmFile::
893 set_no_data_nan(int num_channels) {
894  if (num_channels > 0) {
895  num_channels = min(num_channels, _num_channels);
896  _has_no_data_value = true;
897  _has_no_data_threshold = false;
898  _no_data_value = LPoint4f::zero();
899  PN_float32 nan = make_nan((PN_float32)0.0);
900  for (int i = 0; i < num_channels; ++i) {
901  _no_data_value[i] = nan;
902  }
903  switch (num_channels) {
904  case 1:
905  _has_point = has_point_nan_1;
906  break;
907  case 2:
908  _has_point = has_point_nan_2;
909  break;
910  case 3:
911  _has_point = has_point_nan_3;
912  break;
913  case 4:
914  _has_point = has_point_nan_4;
915  break;
916  default:
917  nassertv(false);
918  }
919  } else {
921  }
922 }
923 
924 ////////////////////////////////////////////////////////////////////
925 // Function: PfmFile::set_no_data_value
926 // Access: Published
927 // Description: Sets the special value that means "no data" when it
928 // appears in the pfm file.
929 ////////////////////////////////////////////////////////////////////
930 void PfmFile::
931 set_no_data_value(const LPoint4f &no_data_value) {
932  nassertv(is_valid());
933 
934  _has_no_data_value = true;
935  _has_no_data_threshold = false;
936  _no_data_value = no_data_value;
937  switch (_num_channels) {
938  case 1:
939  _has_point = has_point_1;
940  break;
941  case 2:
942  _has_point = has_point_2;
943  break;
944  case 3:
945  _has_point = has_point_3;
946  break;
947  case 4:
948  _has_point = has_point_4;
949  break;
950  default:
951  nassertv(false);
952  }
953 }
954 
955 ////////////////////////////////////////////////////////////////////
956 // Function: PfmFile::set_no_data_threshold
957 // Access: Published
958 // Description: Sets the special threshold value. Points that are
959 // below this value in all components are considered "no
960 // value".
961 ////////////////////////////////////////////////////////////////////
962 void PfmFile::
963 set_no_data_threshold(const LPoint4f &no_data_value) {
964  nassertv(is_valid());
965 
966  _has_no_data_value = true;
967  _has_no_data_threshold = true;
968  _no_data_value = no_data_value;
969  switch (_num_channels) {
970  case 1:
971  _has_point = has_point_threshold_1;
972  break;
973  case 2:
974  _has_point = has_point_threshold_2;
975  break;
976  case 3:
977  _has_point = has_point_threshold_3;
978  break;
979  case 4:
980  _has_point = has_point_threshold_4;
981  break;
982  default:
983  nassertv(false);
984  }
985 }
986 
987 ////////////////////////////////////////////////////////////////////
988 // Function: PfmFile::resize
989 // Access: Published
990 // Description: Applies a simple filter to resample the pfm file
991 // in-place to the indicated size. Don't confuse this
992 // with applying a scale to all of the points via
993 // xform().
994 ////////////////////////////////////////////////////////////////////
995 void PfmFile::
996 resize(int new_x_size, int new_y_size) {
997  if (_x_size == 0 || _y_size == 0 || new_x_size == 0 || new_y_size == 0) {
998  clear(new_x_size, new_y_size, _num_channels);
999  return;
1000  }
1001 
1002  if (new_x_size == _x_size && new_y_size == _y_size) {
1003  return;
1004  }
1005 
1006  PfmFile result;
1007  result.clear(new_x_size, new_y_size, _num_channels);
1008  if (_has_no_data_value) {
1009  result.fill(_no_data_value);
1010  }
1011 
1012  if (pfm_resize_quick && new_x_size <= _x_size && new_y_size <= _y_size) {
1013  // If we're downscaling, we can use quick_filter, which is faster.
1014  result.quick_filter_from(*this);
1015 
1016  } else {
1017  // Otherwise, we should use box_filter() or gaussian_filter, which
1018  // are more general.
1019  if (pfm_resize_gaussian) {
1020  result.gaussian_filter_from(pfm_resize_radius, *this);
1021  } else {
1022  result.box_filter_from(pfm_resize_radius, *this);
1023  }
1024  }
1025 
1026  _table.swap(result._table);
1027  _x_size = new_x_size;
1028  _y_size = new_y_size;
1029 }
1030 
1031 ////////////////////////////////////////////////////////////////////
1032 // Function: PfmFile::quick_filter_from
1033 // Access: Public
1034 // Description: Resizes from the given image, with a fixed radius of
1035 // 0.5. This is a very specialized and simple algorithm
1036 // that doesn't handle dropping below the Nyquist rate
1037 // very well, but is quite a bit faster than the more
1038 // general box_filter(), above.
1039 ////////////////////////////////////////////////////////////////////
1040 void PfmFile::
1042  if (_x_size == 0 || _y_size == 0) {
1043  return;
1044  }
1045 
1046  Table new_data;
1047  new_data.reserve(_table.size());
1048 
1049  PN_float32 from_x0, from_x1, from_y0, from_y1;
1050 
1051  int orig_x_size = from.get_x_size();
1052  int orig_y_size = from.get_y_size();
1053 
1054  PN_float32 x_scale = 1.0;
1055  PN_float32 y_scale = 1.0;
1056 
1057  if (_x_size > 1) {
1058  x_scale = (PN_float32)orig_x_size / (PN_float32)_x_size;
1059  }
1060  if (_y_size > 1) {
1061  y_scale = (PN_float32)orig_y_size / (PN_float32)_y_size;
1062  }
1063 
1064  switch (_num_channels) {
1065  case 1:
1066  {
1067  from_y0 = 0.0;
1068  for (int to_y = 0; to_y < _y_size; ++to_y) {
1069  from_y1 = (to_y + 1.0) * y_scale;
1070  from_y1 = min(from_y1, (PN_float32)orig_y_size);
1071 
1072  from_x0 = 0.0;
1073  for (int to_x = 0; to_x < _x_size; ++to_x) {
1074  from_x1 = (to_x + 1.0) * x_scale;
1075  from_x1 = min(from_x1, (PN_float32)orig_x_size);
1076 
1077  // Now the box from (from_x0, from_y0) - (from_x1, from_y1)
1078  // but not including (from_x1, from_y1) maps to the pixel (to_x, to_y).
1079  PN_float32 result;
1080  from.box_filter_region(result, from_x0, from_y0, from_x1, from_y1);
1081  new_data.push_back(result);
1082 
1083  from_x0 = from_x1;
1084  }
1085  from_y0 = from_y1;
1086  }
1087  }
1088  break;
1089 
1090  case 2:
1091  {
1092  from_y0 = 0.0;
1093  for (int to_y = 0; to_y < _y_size; ++to_y) {
1094  from_y1 = (to_y + 1.0) * y_scale;
1095  from_y1 = min(from_y1, (PN_float32)orig_y_size);
1096 
1097  from_x0 = 0.0;
1098  for (int to_x = 0; to_x < _x_size; ++to_x) {
1099  from_x1 = (to_x + 1.0) * x_scale;
1100  from_x1 = min(from_x1, (PN_float32)orig_x_size);
1101 
1102  // Now the box from (from_x0, from_y0) - (from_x1, from_y1)
1103  // but not including (from_x1, from_y1) maps to the pixel (to_x, to_y).
1104  LPoint2f result;
1105  from.box_filter_region(result, from_x0, from_y0, from_x1, from_y1);
1106  new_data.push_back(result[0]);
1107  new_data.push_back(result[1]);
1108 
1109  from_x0 = from_x1;
1110  }
1111  from_y0 = from_y1;
1112  }
1113  }
1114  break;
1115 
1116  case 3:
1117  {
1118  from_y0 = 0.0;
1119  for (int to_y = 0; to_y < _y_size; ++to_y) {
1120  from_y1 = (to_y + 1.0) * y_scale;
1121  from_y1 = min(from_y1, (PN_float32)orig_y_size);
1122 
1123  from_x0 = 0.0;
1124  for (int to_x = 0; to_x < _x_size; ++to_x) {
1125  from_x1 = (to_x + 1.0) * x_scale;
1126  from_x1 = min(from_x1, (PN_float32)orig_x_size);
1127 
1128  // Now the box from (from_x0, from_y0) - (from_x1, from_y1)
1129  // but not including (from_x1, from_y1) maps to the pixel (to_x, to_y).
1130  LPoint3f result;
1131  from.box_filter_region(result, from_x0, from_y0, from_x1, from_y1);
1132  new_data.push_back(result[0]);
1133  new_data.push_back(result[1]);
1134  new_data.push_back(result[2]);
1135 
1136  from_x0 = from_x1;
1137  }
1138  from_y0 = from_y1;
1139  }
1140  }
1141  break;
1142 
1143  case 4:
1144  {
1145  from_y0 = 0.0;
1146  for (int to_y = 0; to_y < _y_size; ++to_y) {
1147  from_y1 = (to_y + 1.0) * y_scale;
1148  from_y1 = min(from_y1, (PN_float32)orig_y_size);
1149 
1150  from_x0 = 0.0;
1151  for (int to_x = 0; to_x < _x_size; ++to_x) {
1152  from_x1 = (to_x + 1.0) * x_scale;
1153  from_x1 = min(from_x1, (PN_float32)orig_x_size);
1154 
1155  // Now the box from (from_x0, from_y0) - (from_x1, from_y1)
1156  // but not including (from_x1, from_y1) maps to the pixel (to_x, to_y).
1157  LPoint4f result;
1158  from.box_filter_region(result, from_x0, from_y0, from_x1, from_y1);
1159  new_data.push_back(result[0]);
1160  new_data.push_back(result[1]);
1161  new_data.push_back(result[2]);
1162  new_data.push_back(result[3]);
1163 
1164  from_x0 = from_x1;
1165  }
1166  from_y0 = from_y1;
1167  }
1168  }
1169  break;
1170 
1171  default:
1172  nassertv(false);
1173  }
1174 
1175  new_data.push_back(0.0);
1176  new_data.push_back(0.0);
1177  new_data.push_back(0.0);
1178  new_data.push_back(0.0);
1179 
1180  nassertv(new_data.size() == _table.size());
1181  _table.swap(new_data);
1182 }
1183 
1184 ////////////////////////////////////////////////////////////////////
1185 // Function: PfmFile::reverse_rows
1186 // Access: Published
1187 // Description: Performs an in-place reversal of the row (y) data.
1188 ////////////////////////////////////////////////////////////////////
1189 void PfmFile::
1191  nassertv(is_valid());
1192 
1193  Table reversed;
1194  reversed.reserve(_table.size());
1195  int row_size = _x_size * _num_channels;
1196  for (int yi = 0; yi < _y_size; ++yi) {
1197  int source_yi = _y_size - 1 - yi;
1198  int start = source_yi * row_size;
1199  reversed.insert(reversed.end(),
1200  _table.begin() + start, _table.begin() + start + row_size);
1201  }
1202 
1203  nassertv(reversed.size() <= _table.size());
1204  // Also add in the extra buffer at the end.
1205  reversed.insert(reversed.end(), _table.size() - reversed.size(), (PN_float32)0.0);
1206 
1207  _table.swap(reversed);
1208 }
1209 
1210 ////////////////////////////////////////////////////////////////////
1211 // Function: PfmFile::flip
1212 // Access: Published
1213 // Description: Reverses, transposes, and/or rotates the table
1214 // in-place according to the specified parameters. If
1215 // flip_x is true, the x axis is reversed; if flip_y is
1216 // true, the y axis is reversed. Then, if transpose is
1217 // true, the x and y axes are exchanged. These
1218 // parameters can be used to select any combination of
1219 // 90-degree or 180-degree rotations and flips.
1220 ////////////////////////////////////////////////////////////////////
1221 void PfmFile::
1222 flip(bool flip_x, bool flip_y, bool transpose) {
1223  nassertv(is_valid());
1224 
1225  Table flipped;
1226  flipped.reserve(_table.size());
1227 
1228  if (transpose) {
1229  // Transposed case. X becomes Y, Y becomes X.
1230  for (int xi = 0; xi < _x_size; ++xi) {
1231  int source_xi = !flip_x ? xi : _x_size - 1 - xi;
1232  for (int yi = 0; yi < _y_size; ++yi) {
1233  int source_yi = !flip_y ? yi : _y_size - 1 - yi;
1234  const PN_float32 *p = &_table[(source_yi * _x_size + source_xi) * _num_channels];
1235  for (int ci = 0; ci < _num_channels; ++ci) {
1236  flipped.push_back(p[ci]);
1237  }
1238  }
1239  }
1240 
1241  int t = _x_size;
1242  _x_size = _y_size;
1243  _y_size = t;
1244 
1245  } else {
1246  // Non-transposed. X is X, Y is Y.
1247  for (int yi = 0; yi < _y_size; ++yi) {
1248  int source_yi = !flip_y ? yi : _y_size - 1 - yi;
1249  for (int xi = 0; xi < _x_size; ++xi) {
1250  int source_xi = !flip_x ? xi : _x_size - 1 - xi;
1251  const PN_float32 *p = &_table[(source_yi * _x_size + source_xi) * _num_channels];
1252  for (int ci = 0; ci < _num_channels; ++ci) {
1253  flipped.push_back(p[ci]);
1254  }
1255  }
1256  }
1257  }
1258 
1259  nassertv(flipped.size() <= _table.size());
1260  // Also add in the extra buffer at the end.
1261  flipped.insert(flipped.end(), _table.size() - flipped.size(), (PN_float32)0.0);
1262 
1263  _table.swap(flipped);
1264 }
1265 
1266 ////////////////////////////////////////////////////////////////////
1267 // Function: PfmFile::xform
1268 // Access: Published
1269 // Description: Applies the indicated transform matrix to all points
1270 // in-place.
1271 ////////////////////////////////////////////////////////////////////
1272 void PfmFile::
1273 xform(const LMatrix4f &transform) {
1274  nassertv(is_valid());
1275 
1276  for (int yi = 0; yi < _y_size; ++yi) {
1277  for (int xi = 0; xi < _x_size; ++xi) {
1278  if (!has_point(xi, yi)) {
1279  continue;
1280  }
1281  LPoint3f &p = modify_point(xi, yi);
1282  transform.xform_point_general_in_place(p);
1283  }
1284  }
1285 }
1286 
1287 ////////////////////////////////////////////////////////////////////
1288 // Function: PfmFile::forward_distort
1289 // Access: Published
1290 // Description: Applies the distortion indicated in the supplied dist
1291 // map to the current map. The dist map is understood
1292 // to be a mapping of points in the range 0..1 in the
1293 // first two dimensions.
1294 //
1295 // The operation can be expressed symbolically as:
1296 //
1297 // this(u, v) = this(dist(u, v))
1298 //
1299 // If scale_factor is not 1, it should be a value > 1,
1300 // and it specifies the factor to upscale the working
1301 // table while processing, to reduce artifacts from
1302 // integer truncation.
1303 //
1304 // By convention, the y axis is inverted in the
1305 // distortion map relative to the coordinates here. A y
1306 // value of 0 in the distortion map corresponds with a v
1307 // value of 1 in this file.
1308 ////////////////////////////////////////////////////////////////////
1309 void PfmFile::
1310 forward_distort(const PfmFile &dist, PN_float32 scale_factor) {
1311  int working_x_size = (int)cceil(_x_size * scale_factor);
1312  int working_y_size = (int)cceil(_y_size * scale_factor);
1313 
1314  working_x_size = max(working_x_size, dist.get_x_size());
1315  working_y_size = max(working_y_size, dist.get_y_size());
1316 
1317  const PfmFile *source_p = this;
1318  PfmFile scaled_source;
1319  if ((*this).get_x_size() != working_x_size || (*this).get_y_size() != working_y_size) {
1320  // Rescale the source file as needed.
1321  scaled_source = (*this);
1322  scaled_source.resize(working_x_size, working_y_size);
1323  source_p = &scaled_source;
1324  }
1325 
1326  const PfmFile *dist_p = &dist;
1327  PfmFile scaled_dist;
1328  if (dist.get_x_size() != working_x_size || dist.get_y_size() != working_y_size) {
1329  // Rescale the dist file as needed.
1330  scaled_dist = dist;
1331  scaled_dist.resize(working_x_size, working_y_size);
1332  dist_p = &scaled_dist;
1333  }
1334 
1335  PfmFile result;
1336  result.clear(working_x_size, working_y_size, _num_channels);
1337 
1338  if (_has_no_data_value) {
1339  result.set_no_data_value(_no_data_value);
1340  result.fill(_no_data_value);
1341  }
1342 
1343  for (int yi = 0; yi < working_y_size; ++yi) {
1344  for (int xi = 0; xi < working_x_size; ++xi) {
1345  if (!dist_p->has_point(xi, yi)) {
1346  continue;
1347  }
1348  LPoint2f uv = dist_p->get_point2(xi, yi);
1349  LPoint3f p;
1350  if (!source_p->calc_bilinear_point(p, uv[0], 1.0 - uv[1])) {
1351  continue;
1352  }
1353  nassertv(!p.is_nan());
1354  result.set_point(xi, working_y_size - 1 - yi, p);
1355  }
1356  }
1357 
1358  // Resize to the target size for completion.
1359  result.resize(_x_size, _y_size);
1360 
1361  nassertv(result._table.size() == _table.size());
1362  _table.swap(result._table);
1363 }
1364 
1365 ////////////////////////////////////////////////////////////////////
1366 // Function: PfmFile::reverse_distort
1367 // Access: Published
1368 // Description: Applies the distortion indicated in the supplied dist
1369 // map to the current map. The dist map is understood
1370 // to be a mapping of points in the range 0..1 in the
1371 // first two dimensions.
1372 //
1373 // The operation can be expressed symbolically as:
1374 //
1375 // this(u, v) = dist(this(u, v))
1376 //
1377 // If scale_factor is not 1, it should be a value > 1,
1378 // and it specifies the factor to upscale the working
1379 // table while processing, to reduce artifacts from
1380 // integer truncation.
1381 //
1382 // By convention, the y axis in inverted in the
1383 // distortion map relative to the coordinates here. A y
1384 // value of 0 in the distortion map corresponds with a v
1385 // value of 1 in this file.
1386 ////////////////////////////////////////////////////////////////////
1387 void PfmFile::
1388 reverse_distort(const PfmFile &dist, PN_float32 scale_factor) {
1389  int working_x_size = (int)cceil(_x_size * scale_factor);
1390  int working_y_size = (int)cceil(_y_size * scale_factor);
1391 
1392  working_x_size = max(working_x_size, dist.get_x_size());
1393  working_y_size = max(working_y_size, dist.get_y_size());
1394 
1395  const PfmFile *source_p = this;
1396  PfmFile scaled_source;
1397  if ((*this).get_x_size() != working_x_size || (*this).get_y_size() != working_y_size) {
1398  // Rescale the source file as needed.
1399  scaled_source = (*this);
1400  scaled_source.resize(working_x_size, working_y_size);
1401  source_p = &scaled_source;
1402  }
1403 
1404  const PfmFile *dist_p = &dist;
1405  PfmFile scaled_dist;
1406  if (dist.get_x_size() != working_x_size || dist.get_y_size() != working_y_size) {
1407  // Rescale the dist file as needed.
1408  scaled_dist = dist;
1409  scaled_dist.resize(working_x_size, working_y_size);
1410  dist_p = &scaled_dist;
1411  }
1412 
1413  PfmFile result;
1414  result.clear(working_x_size, working_y_size, _num_channels);
1415 
1416  if (_has_no_data_value) {
1417  result.set_no_data_value(_no_data_value);
1418  result.fill(_no_data_value);
1419  }
1420 
1421  for (int yi = 0; yi < working_y_size; ++yi) {
1422  for (int xi = 0; xi < working_x_size; ++xi) {
1423  if (!source_p->has_point(xi, yi)) {
1424  continue;
1425  }
1426  LPoint2f uv = source_p->get_point2(xi, yi);
1427  LPoint3f p;
1428  if (!dist_p->calc_bilinear_point(p, uv[0], 1.0 - uv[1])) {
1429  continue;
1430  }
1431  result.set_point(xi, yi, LPoint3f(p[0], 1.0 - p[1], p[2]));
1432  }
1433  }
1434 
1435  // Resize to the target size for completion.
1436  result.resize(_x_size, _y_size);
1437 
1438  nassertv(result._table.size() == _table.size());
1439  _table.swap(result._table);
1440 }
1441 
1442 ////////////////////////////////////////////////////////////////////
1443 // Function: PfmFile::merge
1444 // Access: Published
1445 // Description: Wherever there is missing data in this PfmFile (that
1446 // is, wherever has_point() returns false), copy data
1447 // from the other PfmFile, which must be exactly the
1448 // same dimensions as this one.
1449 ////////////////////////////////////////////////////////////////////
1450 void PfmFile::
1451 merge(const PfmFile &other) {
1452  nassertv(is_valid() && other.is_valid());
1453  nassertv(other._x_size == _x_size && other._y_size == _y_size);
1454 
1455  if (!_has_no_data_value) {
1456  // Trivial no-op.
1457  return;
1458  }
1459 
1460  for (int yi = 0; yi < _y_size; ++yi) {
1461  for (int xi = 0; xi < _x_size; ++xi) {
1462  if (!has_point(xi, yi) && other.has_point(xi, yi)) {
1463  set_point(xi, yi, other.get_point(xi, yi));
1464  }
1465  }
1466  }
1467 }
1468 
1469 ////////////////////////////////////////////////////////////////////
1470 // Function: PfmFile::copy_channel
1471 // Access: Published
1472 // Description: Copies just the specified channel values from the
1473 // indicated PfmFile (which could be same as this
1474 // PfmFile) into the specified channel of this one.
1475 ////////////////////////////////////////////////////////////////////
1476 void PfmFile::
1477 copy_channel(int to_channel, const PfmFile &other, int from_channel) {
1478  nassertv(is_valid() && other.is_valid());
1479  nassertv(other._x_size == _x_size && other._y_size == _y_size);
1480  nassertv(to_channel >= 0 && to_channel < get_num_channels() &&
1481  from_channel >= 0 && from_channel < other.get_num_channels());
1482 
1483  for (int yi = 0; yi < _y_size; ++yi) {
1484  for (int xi = 0; xi < _x_size; ++xi) {
1485  set_channel(xi, yi, to_channel, other.get_channel(xi, yi, from_channel));
1486  }
1487  }
1488 }
1489 
1490 ////////////////////////////////////////////////////////////////////
1491 // Function: PfmFile::copy_channel_masked
1492 // Access: Published
1493 // Description: Copies just the specified channel values from the
1494 // indicated PfmFile, but only where the other file has
1495 // a data point.
1496 ////////////////////////////////////////////////////////////////////
1497 void PfmFile::
1498 copy_channel_masked(int to_channel, const PfmFile &other, int from_channel) {
1499  nassertv(is_valid() && other.is_valid());
1500  nassertv(other._x_size == _x_size && other._y_size == _y_size);
1501  nassertv(to_channel >= 0 && to_channel < get_num_channels() &&
1502  from_channel >= 0 && from_channel < other.get_num_channels());
1503 
1504  for (int yi = 0; yi < _y_size; ++yi) {
1505  for (int xi = 0; xi < _x_size; ++xi) {
1506  if (other.has_point(xi, yi)) {
1507  set_channel(xi, yi, to_channel, other.get_channel(xi, yi, from_channel));
1508  }
1509  }
1510  }
1511 }
1512 
1513 ////////////////////////////////////////////////////////////////////
1514 // Function: PfmFile::apply_crop
1515 // Access: Published
1516 // Description: Reduces the PFM file to the cells in the rectangle
1517 // bounded by (x_begin, x_end, y_begin, y_end), where
1518 // the _end cells are not included.
1519 ////////////////////////////////////////////////////////////////////
1520 void PfmFile::
1521 apply_crop(int x_begin, int x_end, int y_begin, int y_end) {
1522  nassertv(x_begin >= 0 && x_begin <= x_end && x_end <= _x_size);
1523  nassertv(y_begin >= 0 && y_begin <= y_end && y_end <= _y_size);
1524 
1525  int new_x_size = x_end - x_begin;
1526  int new_y_size = y_end - y_begin;
1527  Table new_table;
1528  int new_size = new_x_size * new_y_size * _num_channels;
1529 
1530  // We allocate a little bit bigger to allow safe overflow: you can
1531  // call get_point3() or get_point4() on the last point of a 1- or
1532  // 3-channel image.
1533  new_table.insert(new_table.end(), new_size + 4, (PN_float32)0.0);
1534 
1535  for (int yi = 0; yi < new_y_size; ++yi) {
1536  memcpy(&new_table[(yi * new_x_size) * _num_channels],
1537  &_table[((yi + y_begin) * _x_size + x_begin) * _num_channels],
1538  new_x_size * sizeof(PN_float32) * _num_channels);
1539  }
1540 
1541  nassertv(new_table.size() == new_size + 4);
1542  _table.swap(new_table);
1543  _x_size = new_x_size;
1544  _y_size = new_y_size;
1545 }
1546 
1547 ////////////////////////////////////////////////////////////////////
1548 // Function: PfmFile::clear_to_texcoords
1549 // Access: Published
1550 // Description: Replaces this PfmFile with a new PfmFile of size
1551 // x_size x y_size x 3, containing the x y 0 values in
1552 // the range 0 .. 1 according to the x y index.
1553 ////////////////////////////////////////////////////////////////////
1554 void PfmFile::
1555 clear_to_texcoords(int x_size, int y_size) {
1556  clear(x_size, y_size, 3);
1557 
1558  LPoint2f uv_scale(1.0, 1.0);
1559  if (_x_size > 0) {
1560  uv_scale[0] = 1.0f / PN_float32(_x_size);
1561  }
1562  if (_y_size > 0) {
1563  uv_scale[1] = 1.0f / PN_float32(_y_size);
1564  }
1565 
1566  for (int yi = 0; yi < _y_size; ++yi) {
1567  for (int xi = 0; xi < _x_size; ++xi) {
1568  LPoint3f uv((PN_float32(xi) + 0.5) * uv_scale[0],
1569  (PN_float32(yi) + 0.5) * uv_scale[1],
1570  0.0f);
1571  set_point(xi, yi, uv);
1572  }
1573  }
1574 }
1575 
1576 ////////////////////////////////////////////////////////////////////
1577 // Function: PfmFile::pull_spot
1578 // Access: Published
1579 // Description: Applies delta * t to the point values within radius
1580 // (xr, yr) distance of (xc, yc). The t value is scaled
1581 // from 1.0 at the center to 0.0 at radius (xr, yr), and
1582 // this scale follows the specified exponent. Returns
1583 // the number of points affected.
1584 ////////////////////////////////////////////////////////////////////
1585 int PfmFile::
1586 pull_spot(const LPoint4f &delta, float xc, float yc,
1587  float xr, float yr, float exponent) {
1588  int minx = max((int)cceil(xc - xr), 0);
1589  int maxx = min((int)cfloor(xc + xr), _x_size - 1);
1590  int miny = max((int)cceil(yc - yr), 0);
1591  int maxy = min((int)cfloor(yc + yr), _y_size - 1);
1592 
1593  int count = 0;
1594  for (int yi = miny; yi <= maxy; ++yi) {
1595  for (int xi = minx; xi <= maxx; ++xi) {
1596  float xd = ((float)xi - xc) / xr;
1597  float yd = ((float)yi - yc) / yr;
1598  float r2 = xd * xd + yd * yd;
1599  if (r2 >= 1.0f) {
1600  continue;
1601  }
1602  PN_float32 t = (PN_float32)cpow(1.0f - csqrt(r2), exponent);
1603 
1604  PN_float32 *f = &_table[(yi * _x_size + xi) * _num_channels];
1605  for (int ci = 0; ci < _num_channels; ++ci) {
1606  f[ci] += delta[ci] * t;
1607  }
1608  ++count;
1609  }
1610  }
1611 
1612  return count;
1613 }
1614 
1615 ////////////////////////////////////////////////////////////////////
1616 // Function: PfmFile::calc_tight_bounds
1617 // Access: Published
1618 // Description: Calculates the minimum and maximum vertices of all
1619 // points within the table. Assumes the table contains
1620 // 3-D points.
1621 //
1622 // The return value is true if any points in the table,
1623 // or false if none are.
1624 ////////////////////////////////////////////////////////////////////
1625 bool PfmFile::
1626 calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point) const {
1627  min_point.set(0.0f, 0.0f, 0.0f);
1628  max_point.set(0.0f, 0.0f, 0.0f);
1629 
1630  bool found_any = false;
1631  for (int yi = 0; yi < _y_size; ++yi) {
1632  for (int xi = 0; xi < _x_size; ++xi) {
1633  if (!has_point(xi, yi)) {
1634  continue;
1635  }
1636 
1637  const LPoint3f &point = get_point(xi, yi);
1638  if (!found_any) {
1639  min_point = point;
1640  max_point = point;
1641  found_any = true;
1642  } else {
1643  min_point.set(min(min_point[0], point[0]),
1644  min(min_point[1], point[1]),
1645  min(min_point[2], point[2]));
1646  max_point.set(max(max_point[0], point[0]),
1647  max(max_point[1], point[1]),
1648  max(max_point[2], point[2]));
1649  }
1650  }
1651  }
1652 
1653  return found_any;
1654 }
1655 
1656 ////////////////////////////////////////////////////////////////////
1657 // Function: PfmFile::compute_planar_bounds
1658 // Access: Published
1659 // Description: Computes the minmax bounding volume of the points in
1660 // 3-D space, assuming the points represent a
1661 // mostly-planar surface.
1662 //
1663 // This algorithm works by sampling the (square)
1664 // sample_radius pixels at the four point_dist corners
1665 // around the center (cx - pd, cx + pd) and so on, to
1666 // approximate the plane of the surface. Then all of
1667 // the points are projected into that plane and the
1668 // bounding volume of the entire mesh within that plane
1669 // is determined. If points_only is true, the bounding
1670 // volume of only those four points is determined.
1671 //
1672 // center, point_dist and sample_radius are in UV space,
1673 // i.e. in the range 0..1.
1674 ////////////////////////////////////////////////////////////////////
1675 PT(BoundingHexahedron) PfmFile::
1676 compute_planar_bounds(const LPoint2f &center, PN_float32 point_dist, PN_float32 sample_radius, bool points_only) const {
1677  LPoint3f p0, p1, p2, p3;
1678  compute_sample_point(p0, center[0] + point_dist, center[1] - point_dist, sample_radius);
1679  compute_sample_point(p1, center[0] + point_dist, center[1] + point_dist, sample_radius);
1680  compute_sample_point(p2, center[0] - point_dist, center[1] + point_dist, sample_radius);
1681  compute_sample_point(p3, center[0] - point_dist, center[1] - point_dist, sample_radius);
1682 
1683  LPoint3f normal;
1684 
1685  normal[0] = p0[1] * p1[2] - p0[2] * p1[1];
1686  normal[1] = p0[2] * p1[0] - p0[0] * p1[2];
1687  normal[2] = p0[0] * p1[1] - p0[1] * p1[0];
1688 
1689  normal[0] += p1[1] * p2[2] - p1[2] * p2[1];
1690  normal[1] += p1[2] * p2[0] - p1[0] * p2[2];
1691  normal[2] += p1[0] * p2[1] - p1[1] * p2[0];
1692 
1693  normal[0] += p2[1] * p3[2] - p2[2] * p3[1];
1694  normal[1] += p2[2] * p3[0] - p2[0] * p3[2];
1695  normal[2] += p2[0] * p3[1] - p2[1] * p3[0];
1696 
1697  normal[0] += p3[1] * p0[2] - p3[2] * p0[1];
1698  normal[1] += p3[2] * p0[0] - p3[0] * p0[2];
1699  normal[2] += p3[0] * p0[1] - p3[1] * p0[0];
1700 
1701  normal.normalize();
1702 
1703  LVector3f up = (p1 - p0) + (p2 - p3);
1704  LPoint3f pcenter = ((p0 + p1 + p2 + p3) * 0.25);
1705 
1706  // Compute the transform necessary to rotate all of the points into
1707  // the Y = 0 plane.
1708  LMatrix4f rotate;
1709  look_at(rotate, normal, up);
1710 
1711  LMatrix4f rinv;
1712  rinv.invert_from(rotate);
1713 
1714  LPoint3f trans = pcenter * rinv;
1715  rinv.set_row(3, -trans);
1716  rotate.invert_from(rinv);
1717 
1718  // Now determine the minmax.
1719  PN_float32 min_x, min_y, min_z, max_x, max_y, max_z;
1720  bool got_point = false;
1721  if (points_only) {
1722  LPoint3f points[4] = {
1723  p0 * rinv,
1724  p1 * rinv,
1725  p2 * rinv,
1726  p3 * rinv,
1727  };
1728  for (int i = 0; i < 4; ++i) {
1729  const LPoint3f &point = points[i];
1730  if (!got_point) {
1731  min_x = point[0];
1732  min_y = point[1];
1733  min_z = point[2];
1734  max_x = point[0];
1735  max_y = point[1];
1736  max_z = point[2];
1737  got_point = true;
1738  } else {
1739  min_x = min(min_x, point[0]);
1740  min_y = min(min_y, point[1]);
1741  min_z = min(min_z, point[2]);
1742  max_x = max(max_x, point[0]);
1743  max_y = max(max_y, point[1]);
1744  max_z = max(max_z, point[2]);
1745  }
1746  }
1747 
1748  } else {
1749  for (int yi = 0; yi < _y_size; ++yi) {
1750  for (int xi = 0; xi < _x_size; ++xi) {
1751  if (!has_point(xi, yi)) {
1752  continue;
1753  }
1754 
1755  LPoint3f point = get_point(xi, yi) * rinv;
1756  if (!got_point) {
1757  min_x = point[0];
1758  min_y = point[1];
1759  min_z = point[2];
1760  max_x = point[0];
1761  max_y = point[1];
1762  max_z = point[2];
1763  got_point = true;
1764  } else {
1765  min_x = min(min_x, point[0]);
1766  min_y = min(min_y, point[1]);
1767  min_z = min(min_z, point[2]);
1768  max_x = max(max_x, point[0]);
1769  max_y = max(max_y, point[1]);
1770  max_z = max(max_z, point[2]);
1771  }
1772  }
1773  }
1774  }
1775 
1776  PT(BoundingHexahedron) bounds;
1777 
1778  // We create a BoundingHexahedron with the points in a particular
1779  // well-defined order, based on the current coordinate system.
1780  CoordinateSystem cs = get_default_coordinate_system();
1781  switch (cs) {
1782  case CS_yup_right:
1783  bounds = new BoundingHexahedron
1784  (LPoint3(min_x, min_y, min_z), LPoint3(max_x, min_y, min_z),
1785  LPoint3(min_x, max_y, min_z), LPoint3(max_x, max_y, min_z),
1786  LPoint3(min_x, min_y, max_z), LPoint3(max_x, min_y, max_z),
1787  LPoint3(min_x, max_y, max_z), LPoint3(max_x, max_y, max_z));
1788  break;
1789 
1790  case CS_zup_right:
1791  bounds = new BoundingHexahedron
1792  (LPoint3(min_x, min_y, min_z), LPoint3(max_x, min_y, min_z),
1793  LPoint3(min_x, min_y, max_z), LPoint3(max_x, min_y, max_z),
1794  LPoint3(min_x, max_y, min_z), LPoint3(max_x, max_y, min_z),
1795  LPoint3(min_x, max_y, max_z), LPoint3(max_x, max_y, max_z));
1796  break;
1797 
1798  case CS_yup_left:
1799  bounds = new BoundingHexahedron
1800  (LPoint3(max_x, min_y, max_z), LPoint3(min_x, min_y, max_z),
1801  LPoint3(max_x, max_y, max_z), LPoint3(min_x, max_y, max_z),
1802  LPoint3(max_x, min_y, min_z), LPoint3(min_x, min_y, min_z),
1803  LPoint3(max_x, max_y, min_z), LPoint3(min_x, max_y, min_z));
1804  break;
1805 
1806  case CS_zup_left:
1807  bounds = new BoundingHexahedron
1808  (LPoint3(max_x, max_y, min_z), LPoint3(min_x, max_y, min_z),
1809  LPoint3(max_x, max_y, max_z), LPoint3(min_x, max_y, max_z),
1810  LPoint3(max_x, min_y, min_z), LPoint3(min_x, min_y, min_z),
1811  LPoint3(max_x, min_y, max_z), LPoint3(min_x, min_y, max_z));
1812  break;
1813 
1814  default:
1815  nassertr(false, NULL);
1816  }
1817 
1818  // Rotate the bounding volume back into the original space of the
1819  // screen.
1820  bounds->xform(LCAST(PN_stdfloat, rotate));
1821 
1822  return bounds;
1823 }
1824 
1825 ////////////////////////////////////////////////////////////////////
1826 // Function: PfmFile::compute_sample_point
1827 // Access: Published
1828 // Description: Computes the average of all the point within
1829 // sample_radius (manhattan distance) and the indicated
1830 // point.
1831 //
1832 // The point coordinates are given in UV space, in the
1833 // range 0..1.
1834 ////////////////////////////////////////////////////////////////////
1835 void PfmFile::
1836 compute_sample_point(LPoint3f &result,
1837  PN_float32 x, PN_float32 y, PN_float32 sample_radius) const {
1838  x *= _x_size;
1839  y *= _y_size;
1840  PN_float32 xr = sample_radius * _x_size;
1841  PN_float32 yr = sample_radius * _y_size;
1842 
1843  switch (_num_channels) {
1844  case 1:
1845  {
1846  PN_float32 result1;
1847  box_filter_region(result1, x - xr, y - yr, x + xr, y + yr);
1848  result.set(result1, 0.0, 0.0);
1849  }
1850  break;
1851 
1852  case 2:
1853  {
1854  LPoint2f result2;
1855  box_filter_region(result2, x - xr, y - yr, x + xr, y + yr);
1856  result.set(result2[0], result2[1], 0.0);
1857  }
1858  break;
1859 
1860  case 3:
1861  box_filter_region(result, x - xr, y - yr, x + xr, y + yr);
1862  break;
1863 
1864  case 4:
1865  {
1866  LPoint4f result4;
1867  box_filter_region(result4, x - xr, y - yr, x + xr, y + yr);
1868  result.set(result4[0], result4[1], result4[2]);
1869  }
1870  break;
1871 
1872  default:
1873  nassertv(false);
1874  }
1875 }
1876 
1877 ////////////////////////////////////////////////////////////////////
1878 // Function: PfmFile::copy_sub_image
1879 // Access: Published
1880 // Description: Copies a rectangular area of another image into a
1881 // rectangular area of this image. Both images must
1882 // already have been initialized. The upper-left corner
1883 // of the region in both images is specified, and the
1884 // size of the area; if the size is omitted, it defaults
1885 // to the entire other image, or the largest piece that
1886 // will fit.
1887 ////////////////////////////////////////////////////////////////////
1888 void PfmFile::
1889 copy_sub_image(const PfmFile &copy, int xto, int yto,
1890  int xfrom, int yfrom, int x_size, int y_size) {
1891  int xmin, ymin, xmax, ymax;
1892  setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
1893  xmin, ymin, xmax, ymax);
1894 
1895  int x, y;
1896  switch (_num_channels) {
1897  case 1:
1898  {
1899  for (y = ymin; y < ymax; y++) {
1900  for (x = xmin; x < xmax; x++) {
1901  if (copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
1902  set_point1(x, y, copy.get_point1(x - xmin + xfrom, y - ymin + yfrom));
1903  }
1904  }
1905  }
1906  }
1907  break;
1908 
1909  case 2:
1910  {
1911  for (y = ymin; y < ymax; y++) {
1912  for (x = xmin; x < xmax; x++) {
1913  if (copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
1914  set_point2(x, y, copy.get_point2(x - xmin + xfrom, y - ymin + yfrom));
1915  }
1916  }
1917  }
1918  }
1919  break;
1920 
1921  case 3:
1922  {
1923  for (y = ymin; y < ymax; y++) {
1924  for (x = xmin; x < xmax; x++) {
1925  if (copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
1926  set_point(x, y, copy.get_point(x - xmin + xfrom, y - ymin + yfrom));
1927  }
1928  }
1929  }
1930  }
1931  break;
1932 
1933  case 4:
1934  {
1935  for (y = ymin; y < ymax; y++) {
1936  for (x = xmin; x < xmax; x++) {
1937  if (copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
1938  set_point4(x, y, copy.get_point4(x - xmin + xfrom, y - ymin + yfrom));
1939  }
1940  }
1941  }
1942  }
1943  break;
1944  }
1945 }
1946 
1947 ////////////////////////////////////////////////////////////////////
1948 // Function: PfmFile::add_sub_image
1949 // Access: Published
1950 // Description: Behaves like copy_sub_image(), except the copy pixels
1951 // are added to the pixels of the destination, after
1952 // scaling by the specified pixel_scale.
1953 ////////////////////////////////////////////////////////////////////
1954 void PfmFile::
1955 add_sub_image(const PfmFile &copy, int xto, int yto,
1956  int xfrom, int yfrom, int x_size, int y_size,
1957  float pixel_scale) {
1958  int xmin, ymin, xmax, ymax;
1959  setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
1960  xmin, ymin, xmax, ymax);
1961 
1962  int x, y;
1963  switch (_num_channels) {
1964  case 1:
1965  {
1966  for (y = ymin; y < ymax; y++) {
1967  for (x = xmin; x < xmax; x++) {
1968  if (has_point(x, y) && copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
1969  set_point1(x, y, get_point1(x, y) + copy.get_point1(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale);
1970  }
1971  }
1972  }
1973  }
1974  break;
1975 
1976  case 2:
1977  {
1978  for (y = ymin; y < ymax; y++) {
1979  for (x = xmin; x < xmax; x++) {
1980  if (has_point(x, y) && copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
1981  set_point2(x, y, get_point2(x, y) + copy.get_point2(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale);
1982  }
1983  }
1984  }
1985  }
1986  break;
1987 
1988  case 3:
1989  {
1990  for (y = ymin; y < ymax; y++) {
1991  for (x = xmin; x < xmax; x++) {
1992  if (has_point(x, y) && copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
1993  set_point3(x, y, get_point3(x, y) + copy.get_point3(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale);
1994  }
1995  }
1996  }
1997  }
1998  break;
1999 
2000  case 4:
2001  {
2002  for (y = ymin; y < ymax; y++) {
2003  for (x = xmin; x < xmax; x++) {
2004  if (has_point(x, y) && copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
2005  set_point4(x, y, get_point4(x, y) + copy.get_point4(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale);
2006  }
2007  }
2008  }
2009  }
2010  break;
2011  }
2012 }
2013 
2014 ////////////////////////////////////////////////////////////////////
2015 // Function: PfmFile::mult_sub_image
2016 // Access: Published
2017 // Description: Behaves like copy_sub_image(), except the copy pixels
2018 // are multiplied to the pixels of the destination, after
2019 // scaling by the specified pixel_scale.
2020 ////////////////////////////////////////////////////////////////////
2021 void PfmFile::
2022 mult_sub_image(const PfmFile &copy, int xto, int yto,
2023  int xfrom, int yfrom, int x_size, int y_size,
2024  float pixel_scale) {
2025  int xmin, ymin, xmax, ymax;
2026  setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
2027  xmin, ymin, xmax, ymax);
2028 
2029  int x, y;
2030  switch (_num_channels) {
2031  case 1:
2032  {
2033  for (y = ymin; y < ymax; y++) {
2034  for (x = xmin; x < xmax; x++) {
2035  if (has_point(x, y) && copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
2036  set_point1(x, y, get_point1(x, y) * (copy.get_point1(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale));
2037  }
2038  }
2039  }
2040  }
2041  break;
2042 
2043  case 2:
2044  {
2045  for (y = ymin; y < ymax; y++) {
2046  for (x = xmin; x < xmax; x++) {
2047  if (has_point(x, y) && copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
2048  modify_point2(x, y).componentwise_mult(copy.get_point2(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale);
2049  }
2050  }
2051  }
2052  }
2053  break;
2054 
2055  case 3:
2056  {
2057  for (y = ymin; y < ymax; y++) {
2058  for (x = xmin; x < xmax; x++) {
2059  if (has_point(x, y) && copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
2060  modify_point3(x, y).componentwise_mult(copy.get_point3(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale);
2061  }
2062  }
2063  }
2064  }
2065  break;
2066 
2067  case 4:
2068  {
2069  for (y = ymin; y < ymax; y++) {
2070  for (x = xmin; x < xmax; x++) {
2071  if (has_point(x, y) && copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
2072  modify_point4(x, y).componentwise_mult(copy.get_point4(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale);
2073  }
2074  }
2075  }
2076  }
2077  break;
2078  }
2079 }
2080 
2081 ////////////////////////////////////////////////////////////////////
2082 // Function: PfmFile::divide_sub_image
2083 // Access: Published
2084 // Description: Behaves like copy_sub_image(), except the copy pixels
2085 // are divided into the pixels of the destination, after
2086 // scaling by the specified pixel_scale.
2087 // dest(x, y) = dest(x, y) / (copy(x, y) * pixel_scale).
2088 ////////////////////////////////////////////////////////////////////
2089 void PfmFile::
2090 divide_sub_image(const PfmFile &copy, int xto, int yto,
2091  int xfrom, int yfrom, int x_size, int y_size,
2092  float pixel_scale) {
2093  int xmin, ymin, xmax, ymax;
2094  setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
2095  xmin, ymin, xmax, ymax);
2096 
2097  int x, y;
2098  switch (_num_channels) {
2099  case 1:
2100  {
2101  for (y = ymin; y < ymax; y++) {
2102  for (x = xmin; x < xmax; x++) {
2103  if (has_point(x, y) && copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
2104  set_point1(x, y, get_point1(x, y) / copy.get_point1(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale);
2105  }
2106  }
2107  }
2108  }
2109  break;
2110 
2111  case 2:
2112  {
2113  for (y = ymin; y < ymax; y++) {
2114  for (x = xmin; x < xmax; x++) {
2115  if (has_point(x, y) && copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
2116  LPoint2f p = copy.get_point2(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale;
2117  LPoint2f &t = modify_point2(x, y);
2118  t[0] /= p[0];
2119  t[1] /= p[1];
2120  }
2121  }
2122  }
2123  }
2124  break;
2125 
2126  case 3:
2127  {
2128  for (y = ymin; y < ymax; y++) {
2129  for (x = xmin; x < xmax; x++) {
2130  if (has_point(x, y) && copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
2131  LPoint3f p = copy.get_point3(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale;
2132  LPoint3f &t = modify_point3(x, y);
2133  t[0] /= p[0];
2134  t[1] /= p[1];
2135  t[2] /= p[2];
2136  }
2137  }
2138  }
2139  }
2140  break;
2141 
2142  case 4:
2143  {
2144  for (y = ymin; y < ymax; y++) {
2145  for (x = xmin; x < xmax; x++) {
2146  if (has_point(x, y) && copy.has_point(x - xmin + xfrom, y - ymin + yfrom)) {
2147  LPoint4f p = copy.get_point4(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale;
2148  LPoint4f &t = modify_point4(x, y);
2149  t[0] /= p[0];
2150  t[1] /= p[1];
2151  t[2] /= p[2];
2152  t[3] /= p[3];
2153  }
2154  }
2155  }
2156  }
2157  break;
2158  }
2159 }
2160 
2161 ////////////////////////////////////////////////////////////////////
2162 // Function: PfmFile::operator *=
2163 // Access: Published
2164 // Description: Multiplies every point value in the image by
2165 // a constant floating-point multiplier value.
2166 ////////////////////////////////////////////////////////////////////
2167 void PfmFile::
2168 operator *= (float multiplier) {
2169  nassertv(is_valid());
2170 
2171  switch (_num_channels) {
2172  case 1:
2173  {
2174  for (int y = 0; y < _y_size; ++y) {
2175  for (int x = 0; x < _x_size; ++x) {
2176  if (has_point(x, y)) {
2177  PN_float32 p = get_point1(x, y);
2178  p *= multiplier;
2179  set_point1(x, y, p);
2180  }
2181  }
2182  }
2183  }
2184  break;
2185 
2186  case 2:
2187  {
2188  for (int y = 0; y < _y_size; ++y) {
2189  for (int x = 0; x < _x_size; ++x) {
2190  if (has_point(x, y)) {
2191  LPoint2f &p = modify_point2(x, y);
2192  p *= multiplier;
2193  }
2194  }
2195  }
2196  }
2197  break;
2198 
2199  case 3:
2200  {
2201  for (int y = 0; y < _y_size; ++y) {
2202  for (int x = 0; x < _x_size; ++x) {
2203  if (has_point(x, y)) {
2204  LPoint3f &p = modify_point3(x, y);
2205  p *= multiplier;
2206  }
2207  }
2208  }
2209  }
2210  break;
2211 
2212  case 4:
2213  {
2214  for (int y = 0; y < _y_size; ++y) {
2215  for (int x = 0; x < _x_size; ++x) {
2216  if (has_point(x, y)) {
2217  LPoint4f &p = modify_point4(x, y);
2218  p *= multiplier;
2219  }
2220  }
2221  }
2222  }
2223  break;
2224  }
2225 }
2226 
2227 ////////////////////////////////////////////////////////////////////
2228 // Function: PfmFile::output
2229 // Access: Published
2230 // Description:
2231 ////////////////////////////////////////////////////////////////////
2232 void PfmFile::
2233 output(ostream &out) const {
2234  out << "floating-point image: " << _x_size << " by " << _y_size << " pixels, "
2235  << _num_channels << " channels.";
2236 }
2237 
2238 ////////////////////////////////////////////////////////////////////
2239 // Function: PfmFile::box_filter_region
2240 // Access: Private
2241 // Description: Averages all the points in the rectangle from x0
2242 // .. y0 to x1 .. y1 into result. The region may be
2243 // defined by floating-point boundaries; the result will
2244 // be weighted by the degree of coverage of each
2245 // included point.
2246 ////////////////////////////////////////////////////////////////////
2247 void PfmFile::
2248 box_filter_region(PN_float32 &result,
2249  PN_float32 x0, PN_float32 y0, PN_float32 x1, PN_float32 y1) const {
2250  result = 0.0;
2251  PN_float32 coverage = 0.0;
2252 
2253  if (x1 < x0 || y1 < y0) {
2254  return;
2255  }
2256  nassertv(y0 >= 0.0 && y1 >= 0.0);
2257 
2258  int y = (int)y0;
2259  // Get the first (partial) row
2260  box_filter_line(result, coverage, x0, y, x1, (PN_float32)(y+1)-y0);
2261 
2262  int y_last = (int)y1;
2263  if (y < y_last) {
2264  y++;
2265  while (y < y_last) {
2266  // Get each consecutive (complete) row
2267  box_filter_line(result, coverage, x0, y, x1, 1.0);
2268  y++;
2269  }
2270 
2271  // Get the final (partial) row
2272  PN_float32 y_contrib = y1 - (PN_float32)y_last;
2273  if (y_contrib > 0.0001) {
2274  box_filter_line(result, coverage, x0, y, x1, y_contrib);
2275  }
2276  }
2277 
2278  if (coverage != 0.0) {
2279  result /= coverage;
2280  }
2281 }
2282 
2283 ////////////////////////////////////////////////////////////////////
2284 // Function: PfmFile::box_filter_region
2285 // Access: Private
2286 // Description: Averages all the points in the rectangle from x0
2287 // .. y0 to x1 .. y1 into result. The region may be
2288 // defined by floating-point boundaries; the result will
2289 // be weighted by the degree of coverage of each
2290 // included point.
2291 ////////////////////////////////////////////////////////////////////
2292 void PfmFile::
2293 box_filter_region(LPoint2f &result,
2294  PN_float32 x0, PN_float32 y0, PN_float32 x1, PN_float32 y1) const {
2295  result = LPoint2f::zero();
2296  PN_float32 coverage = 0.0;
2297 
2298  if (x1 < x0 || y1 < y0) {
2299  return;
2300  }
2301  nassertv(y0 >= 0.0 && y1 >= 0.0);
2302 
2303  int y = (int)y0;
2304  // Get the first (partial) row
2305  box_filter_line(result, coverage, x0, y, x1, (PN_float32)(y+1)-y0);
2306 
2307  int y_last = (int)y1;
2308  if (y < y_last) {
2309  y++;
2310  while (y < y_last) {
2311  // Get each consecutive (complete) row
2312  box_filter_line(result, coverage, x0, y, x1, 1.0);
2313  y++;
2314  }
2315 
2316  // Get the final (partial) row
2317  PN_float32 y_contrib = y1 - (PN_float32)y_last;
2318  if (y_contrib > 0.0001) {
2319  box_filter_line(result, coverage, x0, y, x1, y_contrib);
2320  }
2321  }
2322 
2323  if (coverage != 0.0) {
2324  result /= coverage;
2325  }
2326 }
2327 
2328 ////////////////////////////////////////////////////////////////////
2329 // Function: PfmFile::box_filter_region
2330 // Access: Private
2331 // Description: Averages all the points in the rectangle from x0
2332 // .. y0 to x1 .. y1 into result. The region may be
2333 // defined by floating-point boundaries; the result will
2334 // be weighted by the degree of coverage of each
2335 // included point.
2336 ////////////////////////////////////////////////////////////////////
2337 void PfmFile::
2338 box_filter_region(LPoint3f &result,
2339  PN_float32 x0, PN_float32 y0, PN_float32 x1, PN_float32 y1) const {
2340  result = LPoint3f::zero();
2341  PN_float32 coverage = 0.0;
2342 
2343  if (x1 < x0 || y1 < y0) {
2344  return;
2345  }
2346  nassertv(y0 >= 0.0 && y1 >= 0.0);
2347 
2348  int y = (int)y0;
2349  // Get the first (partial) row
2350  box_filter_line(result, coverage, x0, y, x1, (PN_float32)(y+1)-y0);
2351 
2352  int y_last = (int)y1;
2353  if (y < y_last) {
2354  y++;
2355  while (y < y_last) {
2356  // Get each consecutive (complete) row
2357  box_filter_line(result, coverage, x0, y, x1, 1.0);
2358  y++;
2359  }
2360 
2361  // Get the final (partial) row
2362  PN_float32 y_contrib = y1 - (PN_float32)y_last;
2363  if (y_contrib > 0.0001) {
2364  box_filter_line(result, coverage, x0, y, x1, y_contrib);
2365  }
2366  }
2367 
2368  if (coverage != 0.0) {
2369  result /= coverage;
2370  }
2371 }
2372 
2373 ////////////////////////////////////////////////////////////////////
2374 // Function: PfmFile::box_filter_region
2375 // Access: Private
2376 // Description: Averages all the points in the rectangle from x0
2377 // .. y0 to x1 .. y1 into result. The region may be
2378 // defined by floating-point boundaries; the result will
2379 // be weighted by the degree of coverage of each
2380 // included point.
2381 ////////////////////////////////////////////////////////////////////
2382 void PfmFile::
2383 box_filter_region(LPoint4f &result,
2384  PN_float32 x0, PN_float32 y0, PN_float32 x1, PN_float32 y1) const {
2385  result = LPoint4f::zero();
2386  PN_float32 coverage = 0.0;
2387 
2388  if (x1 < x0 || y1 < y0) {
2389  return;
2390  }
2391  nassertv(y0 >= 0.0 && y1 >= 0.0);
2392 
2393  int y = (int)y0;
2394  // Get the first (partial) row
2395  box_filter_line(result, coverage, x0, y, x1, (PN_float32)(y+1)-y0);
2396 
2397  int y_last = (int)y1;
2398  if (y < y_last) {
2399  y++;
2400  while (y < y_last) {
2401  // Get each consecutive (complete) row
2402  box_filter_line(result, coverage, x0, y, x1, 1.0);
2403  y++;
2404  }
2405 
2406  // Get the final (partial) row
2407  PN_float32 y_contrib = y1 - (PN_float32)y_last;
2408  if (y_contrib > 0.0001) {
2409  box_filter_line(result, coverage, x0, y, x1, y_contrib);
2410  }
2411  }
2412 
2413  if (coverage != 0.0) {
2414  result /= coverage;
2415  }
2416 }
2417 
2418 ////////////////////////////////////////////////////////////////////
2419 // Function: PfmFile::box_filter_line
2420 // Access: Private
2421 // Description:
2422 ////////////////////////////////////////////////////////////////////
2423 void PfmFile::
2424 box_filter_line(PN_float32 &result, PN_float32 &coverage,
2425  PN_float32 x0, int y, PN_float32 x1, PN_float32 y_contrib) const {
2426  int x = (int)x0;
2427  // Get the first (partial) xel
2428  box_filter_point(result, coverage, x, y, (PN_float32)(x+1)-x0, y_contrib);
2429 
2430  int x_last = (int)x1;
2431  if (x < x_last) {
2432  x++;
2433  while (x < x_last) {
2434  // Get each consecutive (complete) xel
2435  box_filter_point(result, coverage, x, y, 1.0, y_contrib);
2436  x++;
2437  }
2438 
2439  // Get the final (partial) xel
2440  PN_float32 x_contrib = x1 - (PN_float32)x_last;
2441  if (x_contrib > 0.0001) {
2442  box_filter_point(result, coverage, x, y, x_contrib, y_contrib);
2443  }
2444  }
2445 }
2446 
2447 ////////////////////////////////////////////////////////////////////
2448 // Function: PfmFile::box_filter_line
2449 // Access: Private
2450 // Description:
2451 ////////////////////////////////////////////////////////////////////
2452 void PfmFile::
2453 box_filter_line(LPoint2f &result, PN_float32 &coverage,
2454  PN_float32 x0, int y, PN_float32 x1, PN_float32 y_contrib) const {
2455  int x = (int)x0;
2456  // Get the first (partial) xel
2457  box_filter_point(result, coverage, x, y, (PN_float32)(x+1)-x0, y_contrib);
2458 
2459  int x_last = (int)x1;
2460  if (x < x_last) {
2461  x++;
2462  while (x < x_last) {
2463  // Get each consecutive (complete) xel
2464  box_filter_point(result, coverage, x, y, 1.0, y_contrib);
2465  x++;
2466  }
2467 
2468  // Get the final (partial) xel
2469  PN_float32 x_contrib = x1 - (PN_float32)x_last;
2470  if (x_contrib > 0.0001) {
2471  box_filter_point(result, coverage, x, y, x_contrib, y_contrib);
2472  }
2473  }
2474 }
2475 
2476 ////////////////////////////////////////////////////////////////////
2477 // Function: PfmFile::box_filter_line
2478 // Access: Private
2479 // Description:
2480 ////////////////////////////////////////////////////////////////////
2481 void PfmFile::
2482 box_filter_line(LPoint3f &result, PN_float32 &coverage,
2483  PN_float32 x0, int y, PN_float32 x1, PN_float32 y_contrib) const {
2484  int x = (int)x0;
2485  // Get the first (partial) xel
2486  box_filter_point(result, coverage, x, y, (PN_float32)(x+1)-x0, y_contrib);
2487 
2488  int x_last = (int)x1;
2489  if (x < x_last) {
2490  x++;
2491  while (x < x_last) {
2492  // Get each consecutive (complete) xel
2493  box_filter_point(result, coverage, x, y, 1.0, y_contrib);
2494  x++;
2495  }
2496 
2497  // Get the final (partial) xel
2498  PN_float32 x_contrib = x1 - (PN_float32)x_last;
2499  if (x_contrib > 0.0001) {
2500  box_filter_point(result, coverage, x, y, x_contrib, y_contrib);
2501  }
2502  }
2503 }
2504 
2505 ////////////////////////////////////////////////////////////////////
2506 // Function: PfmFile::box_filter_line
2507 // Access: Private
2508 // Description:
2509 ////////////////////////////////////////////////////////////////////
2510 void PfmFile::
2511 box_filter_line(LPoint4f &result, PN_float32 &coverage,
2512  PN_float32 x0, int y, PN_float32 x1, PN_float32 y_contrib) const {
2513  int x = (int)x0;
2514  // Get the first (partial) xel
2515  box_filter_point(result, coverage, x, y, (PN_float32)(x+1)-x0, y_contrib);
2516 
2517  int x_last = (int)x1;
2518  if (x < x_last) {
2519  x++;
2520  while (x < x_last) {
2521  // Get each consecutive (complete) xel
2522  box_filter_point(result, coverage, x, y, 1.0, y_contrib);
2523  x++;
2524  }
2525 
2526  // Get the final (partial) xel
2527  PN_float32 x_contrib = x1 - (PN_float32)x_last;
2528  if (x_contrib > 0.0001) {
2529  box_filter_point(result, coverage, x, y, x_contrib, y_contrib);
2530  }
2531  }
2532 }
2533 
2534 ////////////////////////////////////////////////////////////////////
2535 // Function: PfmFile::box_filter_point
2536 // Access: Private
2537 // Description:
2538 ////////////////////////////////////////////////////////////////////
2539 void PfmFile::
2540 box_filter_point(PN_float32 &result, PN_float32 &coverage,
2541  int x, int y, PN_float32 x_contrib, PN_float32 y_contrib) const {
2542  if (!has_point(x, y)) {
2543  return;
2544  }
2545  PN_float32 point = get_point1(x, y);
2546 
2547  PN_float32 contrib = x_contrib * y_contrib;
2548  result += point * contrib;
2549  coverage += contrib;
2550 }
2551 
2552 ////////////////////////////////////////////////////////////////////
2553 // Function: PfmFile::box_filter_point
2554 // Access: Private
2555 // Description:
2556 ////////////////////////////////////////////////////////////////////
2557 void PfmFile::
2558 box_filter_point(LPoint2f &result, PN_float32 &coverage,
2559  int x, int y, PN_float32 x_contrib, PN_float32 y_contrib) const {
2560  if (!has_point(x, y)) {
2561  return;
2562  }
2563  const LPoint2f &point = get_point2(x, y);
2564 
2565  PN_float32 contrib = x_contrib * y_contrib;
2566  result += point * contrib;
2567  coverage += contrib;
2568 }
2569 
2570 ////////////////////////////////////////////////////////////////////
2571 // Function: PfmFile::box_filter_point
2572 // Access: Private
2573 // Description:
2574 ////////////////////////////////////////////////////////////////////
2575 void PfmFile::
2576 box_filter_point(LPoint3f &result, PN_float32 &coverage,
2577  int x, int y, PN_float32 x_contrib, PN_float32 y_contrib) const {
2578  if (!has_point(x, y)) {
2579  return;
2580  }
2581  const LPoint3f &point = get_point3(x, y);
2582 
2583  PN_float32 contrib = x_contrib * y_contrib;
2584  result += point * contrib;
2585  coverage += contrib;
2586 }
2587 
2588 ////////////////////////////////////////////////////////////////////
2589 // Function: PfmFile::box_filter_point
2590 // Access: Private
2591 // Description:
2592 ////////////////////////////////////////////////////////////////////
2593 void PfmFile::
2594 box_filter_point(LPoint4f &result, PN_float32 &coverage,
2595  int x, int y, PN_float32 x_contrib, PN_float32 y_contrib) const {
2596  if (!has_point(x, y)) {
2597  return;
2598  }
2599  const LPoint4f &point = get_point4(x, y);
2600 
2601  PN_float32 contrib = x_contrib * y_contrib;
2602  result += point * contrib;
2603  coverage += contrib;
2604 }
2605 
2606 ////////////////////////////////////////////////////////////////////
2607 // Function: PfmFile::fill_mini_grid
2608 // Access: Private
2609 // Description: A support function for calc_average_point(), this
2610 // recursively fills in the holes in the mini_grid data
2611 // with the index to the nearest value.
2612 ////////////////////////////////////////////////////////////////////
2613 void PfmFile::
2614 fill_mini_grid(MiniGridCell *mini_grid, int x_size, int y_size,
2615  int xi, int yi, int dist, int sxi, int syi) const {
2616  if (xi < 0 || xi >= x_size || yi < 0 || yi >= y_size) {
2617  // Out of bounds.
2618  return;
2619  }
2620 
2621  int gi = yi * x_size + xi;
2622  if (mini_grid[gi]._dist == -1 || mini_grid[gi]._dist > dist) {
2623  // Here's an undefined value that we need to populate.
2624  mini_grid[gi]._dist = dist;
2625  mini_grid[gi]._sxi = sxi;
2626  mini_grid[gi]._syi = syi;
2627  fill_mini_grid(mini_grid, x_size, y_size, xi + 1, yi, dist + 1, sxi, syi);
2628  fill_mini_grid(mini_grid, x_size, y_size, xi - 1, yi, dist + 1, sxi, syi);
2629  fill_mini_grid(mini_grid, x_size, y_size, xi, yi + 1, dist + 1, sxi, syi);
2630  fill_mini_grid(mini_grid, x_size, y_size, xi, yi - 1, dist + 1, sxi, syi);
2631  }
2632 }
2633 
2634 ////////////////////////////////////////////////////////////////////
2635 // Function: PfmFile::has_point_noop
2636 // Access: Private, Static
2637 // Description: The implementation of has_point() for
2638 // files without a no_data_value.
2639 ////////////////////////////////////////////////////////////////////
2640 bool PfmFile::
2641 has_point_noop(const PfmFile *self, int x, int y) {
2642  if ((x >= 0 && x < self->_x_size) &&
2643  (y >= 0 && y < self->_y_size)) {
2644  return true;
2645  }
2646  return false;
2647 }
2648 
2649 ////////////////////////////////////////////////////////////////////
2650 // Function: PfmFile::has_point_1
2651 // Access: Private, Static
2652 // Description: The implementation of has_point() for 1-component
2653 // files with a no_data_value.
2654 ////////////////////////////////////////////////////////////////////
2655 bool PfmFile::
2656 has_point_1(const PfmFile *self, int x, int y) {
2657  if ((x >= 0 && x < self->_x_size) &&
2658  (y >= 0 && y < self->_y_size)) {
2659  return self->_table[(y * self->_x_size + x)] != self->_no_data_value[0];
2660  }
2661  return false;
2662 }
2663 
2664 ////////////////////////////////////////////////////////////////////
2665 // Function: PfmFile::has_point_2
2666 // Access: Private, Static
2667 // Description: The implementation of has_point() for 2-component
2668 // files with a no_data_value.
2669 ////////////////////////////////////////////////////////////////////
2670 bool PfmFile::
2671 has_point_2(const PfmFile *self, int x, int y) {
2672  if ((x >= 0 && x < self->_x_size) &&
2673  (y >= 0 && y < self->_y_size)) {
2674  return *(LPoint2f *)&self->_table[(y * self->_x_size + x) * 2] != *(LPoint2f *)&self->_no_data_value;
2675  }
2676  return false;
2677 }
2678 
2679 ////////////////////////////////////////////////////////////////////
2680 // Function: PfmFile::has_point_3
2681 // Access: Private, Static
2682 // Description: The implementation of has_point() for 3-component
2683 // files with a no_data_value.
2684 ////////////////////////////////////////////////////////////////////
2685 bool PfmFile::
2686 has_point_3(const PfmFile *self, int x, int y) {
2687  if ((x >= 0 && x < self->_x_size) &&
2688  (y >= 0 && y < self->_y_size)) {
2689  return *(LPoint3f *)&self->_table[(y * self->_x_size + x) * 3] != *(LPoint3f *)&self->_no_data_value;
2690  }
2691  return false;
2692 }
2693 
2694 ////////////////////////////////////////////////////////////////////
2695 // Function: PfmFile::has_point_4
2696 // Access: Private, Static
2697 // Description: The implementation of has_point() for 4-component
2698 // files with a no_data_value.
2699 ////////////////////////////////////////////////////////////////////
2700 bool PfmFile::
2701 has_point_4(const PfmFile *self, int x, int y) {
2702  if ((x >= 0 && x < self->_x_size) &&
2703  (y >= 0 && y < self->_y_size)) {
2704  return *(LPoint4f *)&self->_table[(y * self->_x_size + x) * 4] != *(LPoint4f *)&self->_no_data_value;
2705  }
2706  return false;
2707 }
2708 
2709 ////////////////////////////////////////////////////////////////////
2710 // Function: PfmFile::has_point_threshold_1
2711 // Access: Private, Static
2712 // Description: The implementation of has_point_threshold() for 1-component
2713 // files with a no_data_value.
2714 ////////////////////////////////////////////////////////////////////
2715 bool PfmFile::
2716 has_point_threshold_1(const PfmFile *self, int x, int y) {
2717  if ((x >= 0 && x < self->_x_size) &&
2718  (y >= 0 && y < self->_y_size)) {
2719  const float *table = &self->_table[(y * self->_x_size + x)];
2720  return table[0] >= self->_no_data_value[0];
2721  }
2722  return false;
2723 }
2724 
2725 ////////////////////////////////////////////////////////////////////
2726 // Function: PfmFile::has_point_threshold_2
2727 // Access: Private, Static
2728 // Description: The implementation of has_point_threshold() for 2-component
2729 // files with a no_data_value.
2730 ////////////////////////////////////////////////////////////////////
2731 bool PfmFile::
2732 has_point_threshold_2(const PfmFile *self, int x, int y) {
2733  if ((x >= 0 && x < self->_x_size) &&
2734  (y >= 0 && y < self->_y_size)) {
2735  const float *table = &self->_table[(y * self->_x_size + x) * 2];
2736  return (table[0] >= self->_no_data_value[0] ||
2737  table[1] >= self->_no_data_value[1]);
2738  }
2739  return false;
2740 }
2741 
2742 ////////////////////////////////////////////////////////////////////
2743 // Function: PfmFile::has_point_threshold_3
2744 // Access: Private, Static
2745 // Description: The implementation of has_point_threshold() for 3-component
2746 // files with a no_data_value.
2747 ////////////////////////////////////////////////////////////////////
2748 bool PfmFile::
2749 has_point_threshold_3(const PfmFile *self, int x, int y) {
2750  if ((x >= 0 && x < self->_x_size) &&
2751  (y >= 0 && y < self->_y_size)) {
2752  const float *table = &self->_table[(y * self->_x_size + x) * 3];
2753  return (table[0] >= self->_no_data_value[0] ||
2754  table[1] >= self->_no_data_value[1] ||
2755  table[2] >= self->_no_data_value[2]);
2756  }
2757  return false;
2758 }
2759 
2760 ////////////////////////////////////////////////////////////////////
2761 // Function: PfmFile::has_point_threshold_4
2762 // Access: Private, Static
2763 // Description: The implementation of has_point_threshold() for 4-component
2764 // files with a no_data_value.
2765 ////////////////////////////////////////////////////////////////////
2766 bool PfmFile::
2767 has_point_threshold_4(const PfmFile *self, int x, int y) {
2768  if ((x >= 0 && x < self->_x_size) &&
2769  (y >= 0 && y < self->_y_size)) {
2770  const float *table = &self->_table[(y * self->_x_size + x) * 4];
2771  return (table[0] >= self->_no_data_value[0] ||
2772  table[1] >= self->_no_data_value[1] ||
2773  table[2] >= self->_no_data_value[2] ||
2774  table[3] >= self->_no_data_value[3]);
2775  }
2776  return false;
2777 }
2778 
2779 ////////////////////////////////////////////////////////////////////
2780 // Function: PfmFile::has_point_chan4
2781 // Access: Private, Static
2782 // Description: The implementation of has_point() for 4-component
2783 // files with set_no_data_chan4() in effect. This means
2784 // that the data is valid iff the fourth channel >= 0.
2785 ////////////////////////////////////////////////////////////////////
2786 bool PfmFile::
2787 has_point_chan4(const PfmFile *self, int x, int y) {
2788  if ((x >= 0 && x < self->_x_size) &&
2789  (y >= 0 && y < self->_y_size)) {
2790  return self->_table[(y * self->_x_size + x) * 4 + 3] >= 0.0;
2791  }
2792  return false;
2793 }
2794 
2795 ////////////////////////////////////////////////////////////////////
2796 // Function: PfmFile::has_point_nan_1
2797 // Access: Private, Static
2798 // Description: The implementation of has_point() for
2799 // files with set_no_data_nan() in effect. This means
2800 // that the data is valid iff no components involve NaN.
2801 ////////////////////////////////////////////////////////////////////
2802 bool PfmFile::
2803 has_point_nan_1(const PfmFile *self, int x, int y) {
2804  if ((x >= 0 && x < self->_x_size) &&
2805  (y >= 0 && y < self->_y_size)) {
2806  return !cnan(self->_table[(y * self->_x_size + x) * self->_num_channels]);
2807  }
2808  return false;
2809 }
2810 
2811 ////////////////////////////////////////////////////////////////////
2812 // Function: PfmFile::has_point_nan_2
2813 // Access: Private, Static
2814 // Description: The implementation of has_point() for
2815 // files with set_no_data_nan() in effect. This means
2816 // that the data is valid iff no components involve NaN.
2817 ////////////////////////////////////////////////////////////////////
2818 bool PfmFile::
2819 has_point_nan_2(const PfmFile *self, int x, int y) {
2820  if ((x >= 0 && x < self->_x_size) &&
2821  (y >= 0 && y < self->_y_size)) {
2822  return !((LVecBase2f *)&self->_table[(y * self->_x_size + x) * self->_num_channels])->is_nan();
2823  }
2824  return false;
2825 }
2826 
2827 ////////////////////////////////////////////////////////////////////
2828 // Function: PfmFile::has_point_nan_3
2829 // Access: Private, Static
2830 // Description: The implementation of has_point() for
2831 // files with set_no_data_nan() in effect. This means
2832 // that the data is valid iff no components involve NaN.
2833 ////////////////////////////////////////////////////////////////////
2834 bool PfmFile::
2835 has_point_nan_3(const PfmFile *self, int x, int y) {
2836  if ((x >= 0 && x < self->_x_size) &&
2837  (y >= 0 && y < self->_y_size)) {
2838  return !((LVecBase3f *)&self->_table[(y * self->_x_size + x) * self->_num_channels])->is_nan();
2839  }
2840  return false;
2841 }
2842 
2843 ////////////////////////////////////////////////////////////////////
2844 // Function: PfmFile::has_point_nan_4
2845 // Access: Private, Static
2846 // Description: The implementation of has_point() for
2847 // files with set_no_data_nan() in effect. This means
2848 // that the data is valid iff no components involve NaN.
2849 ////////////////////////////////////////////////////////////////////
2850 bool PfmFile::
2851 has_point_nan_4(const PfmFile *self, int x, int y) {
2852  if ((x >= 0 && x < self->_x_size) &&
2853  (y >= 0 && y < self->_y_size)) {
2854  return !((LVecBase4f *)&self->_table[(y * self->_x_size + x) * self->_num_channels])->is_nan();
2855  }
2856  return false;
2857 }
bool calc_average_point(LPoint3f &result, PN_float32 x, PN_float32 y, PN_float32 radius) const
Computes the unweighted average point of all points within the box centered at (x, y) with the indicated Manhattan-distance radius.
Definition: pfmFile.cxx:620
void forward_distort(const PfmFile &dist, PN_float32 scale_factor=1.0)
Applies the distortion indicated in the supplied dist map to the current map.
Definition: pfmFile.cxx:1310
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
bool has_point(int x, int y) const
Returns true if there is a valid point at x, y.
Definition: pfmFile.I:57
void copy_header_from(const PNMImageHeader &header)
Initializes all the data in the header (x_size, y_size, num_channels, etc.) to the same values indica...
Definition: pnmWriter.I:103
bool open_write(ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:2045
bool is_column_empty(int x, int y_begin, int y_end) const
Returns true if all of the points on column x, from [y_begin, y_end), are the no_data value...
Definition: pfmFile.cxx:865
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:68
void fill_channel_masked(int channel, PN_float32 value)
Fills the indicated channel with all of the same value, but only where the table already has a data p...
Definition: pfmFile.cxx:580
void mult_sub_image(const PfmFile &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1, float pixel_scale=1.0)
Behaves like copy_sub_image(), except the copy pixels are multiplied to the pixels of the destination...
Definition: pfmFile.cxx:2022
void fill_channel(int channel, PN_float32 value)
Fills the indicated channel with all of the same value, leaving the other channels unchanged...
Definition: pfmFile.cxx:550
void set_point4(int x, int y, const LVecBase4f &point)
Replaces the 4-component point value at the indicated point.
Definition: pfmFile.I:333
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
bool calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point) const
Calculates the minimum and maximum vertices of all points within the table.
Definition: pfmFile.cxx:1626
const LPoint3f & get_point(int x, int y) const
Returns the 3-component point value at the indicated point.
Definition: pfmFile.I:198
float get_gray(int x, int y) const
Returns the gray component color at the indicated pixel.
Definition: pnmImage.I:928
virtual bool write_pfm(const PfmFile &pfm)
Writes floating-point data from the indicated PfmFile.
Definition: pnmWriter.cxx:64
static const LPoint3f & zero()
Returns a zero-length point.
Definition: lpoint3.h:259
bool read(const Filename &filename, PNMFileType *type=NULL, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:245
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:82
bool calc_bilinear_point(LPoint3f &result, PN_float32 x, PN_float32 y) const
Computes the weighted average of the four nearest points to the floating-point index (x...
Definition: pfmFile.cxx:708
void fill_nan()
Fills the table with all NaN.
Definition: pfmFile.cxx:526
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
void fill_channel_masked_nan(int channel)
Fills the indicated channel with NaN, but only where the table already has a data point...
Definition: pfmFile.cxx:604
void fill_channel_nan(int channel)
Fills the indicated channel with NaN, leaving the other channels unchanged.
Definition: pfmFile.cxx:567
xel * get_array()
Directly access the underlying PNMImage array.
Definition: pnmImage.I:1312
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
bool is_nan() const
Returns true if any component of the vector is not-a-number, false otherwise.
Definition: lvecBase3.h:464
void set_point2(int x, int y, const LVecBase2f &point)
Replaces the 2-component point value at the indicated point.
Definition: pfmFile.I:138
static const LVecBase3f & zero()
Returns a zero-length vector.
Definition: lvecBase3.h:382
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:37
static void close_read_file(istream *stream)
Closes a file opened by a previous call to open_read_file().
const LPoint4f & get_point4(int x, int y) const
Returns the 4-component point value at the indicated point.
Definition: pfmFile.I:319
void set_point3(int x, int y, const LVecBase3f &point)
Replaces the 3-component point value at the indicated point.
Definition: pfmFile.I:259
virtual bool supports_floating_point()
Returns true if this PNMFileType can accept a floating-point image type, false if it can only accept ...
Definition: pnmWriter.cxx:40
bool is_row_empty(int y, int x_begin, int x_end) const
Returns true if all of the points on row y, in the range [x_begin, x_end), are the no_data value...
Definition: pfmFile.cxx:841
int get_y_size() const
Returns the number of pixels in the Y direction.
static const LPoint4f & zero()
Returns a zero-length point.
Definition: lpoint4.h:238
int get_x_size() const
Returns the number of pixels in the X direction.
const LPoint3f & get_point3(int x, int y) const
Returns the 3-component point value at the indicated point.
Definition: pfmFile.I:245
virtual bool read_pfm(PfmFile &pfm)
Reads floating-point data directly into the indicated PfmFile.
Definition: pnmReader.cxx:93
void resize(int new_x_size, int new_y_size)
Applies a simple filter to resample the pfm file in-place to the indicated size.
Definition: pfmFile.cxx:996
bool is_valid() const
Returns true if the image has been read in or correctly initialized with a height and width...
Definition: pnmImage.I:309
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
const LPoint2f & get_point2(int x, int y) const
Returns the 2-component point value at the indicated point.
Definition: pfmFile.I:124
LPoint4f & modify_point4(int x, int y)
Returns a modifiable 4-component point value at the indicated point.
Definition: pfmFile.I:375
PN_float32 get_point1(int x, int y) const
Returns the 1-component point value at the indicated point.
Definition: pfmFile.I:96
bool read(const Filename &fullpath)
Reads the PFM data from the indicated file, returning true on success, false on failure.
Definition: pfmFile.cxx:130
void set_point1(int x, int y, PN_float32 point)
Replaces the 1-component point value at the indicated point.
Definition: pfmFile.I:109
bool write(const Filename &fullpath)
Writes the PFM data to the indicated file, returning true on success, false on failure.
Definition: pfmFile.cxx:222
bool is_valid() const
Returns true if the PNMReader can be used to read data, false if something is wrong.
Definition: pnmReader.I:65
void gaussian_filter_from(float radius, const PfmFile &copy)
Makes a resized copy of the indicated image into this one using the indicated filter.
void merge(const PfmFile &other)
Wherever there is missing data in this PfmFile (that is, wherever has_point() returns false)...
Definition: pfmFile.cxx:1451
LRGBColorf get_xel(int x, int y) const
Returns the RGB color at the indicated pixel.
Definition: pnmImage.I:597
LColorf get_xel_a(int x, int y) const
Returns the RGBA color at the indicated pixel.
Definition: pnmImage.I:720
void copy_channel_masked(int to_channel, const PfmFile &other, int from_channel)
Copies just the specified channel values from the indicated PfmFile, but only where the other file ha...
Definition: pfmFile.cxx:1498
void apply_crop(int x_begin, int x_end, int y_begin, int y_end)
Reduces the PFM file to the cells in the rectangle bounded by (x_begin, x_end, y_begin, y_end), where the _end cells are not included.
Definition: pfmFile.cxx:1521
LPoint3f & modify_point3(int x, int y)
Returns a modifiable 3-component point value at the indicated point.
Definition: pfmFile.I:301
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
Defines a pfm file, a 2-d table of floating-point numbers, either 3-component or 1-component, or with a special extension, 2- or 4-component.
Definition: pfmFile.h:34
void reverse_rows()
Performs an in-place reversal of the row (y) data.
Definition: pfmFile.cxx:1190
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
void set_gray(int x, int y, float gray)
Sets the gray component color at the indicated pixel.
Definition: pnmImage.I:994
void box_filter_from(float radius, const PfmFile &copy)
Makes a resized copy of the indicated image into this one using the indicated filter.
void clear_to_texcoords(int x_size, int y_size)
Replaces this PfmFile with a new PfmFile of size x_size x y_size x 3, containing the x y 0 values in ...
Definition: pfmFile.cxx:1555
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
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:68
bool invert_from(const LMatrix4f &other)
Computes the inverse of the other matrix, and stores the result in this matrix.
Definition: lmatrix.h:2173
void set_xel_a(int x, int y, const LColorf &value)
Changes the RGBA color at the indicated pixel.
Definition: pnmImage.I:789
void reverse_distort(const PfmFile &dist, PN_float32 scale_factor=1.0)
Applies the distortion indicated in the supplied dist map to the current map.
Definition: pfmFile.cxx:1388
This is the base class for all two-component vectors and points.
Definition: lvecBase2.h:105
void fill(PN_float32 value)
Fills the table with all of the same value.
Definition: pfmFile.I:391
void operator*=(float multiplier)
Multiplies every point value in the image by a constant floating-point multiplier value...
Definition: pfmFile.cxx:2168
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
void set_point(int x, int y, const LVecBase3f &point)
Replaces the 3-component point value at the indicated point.
Definition: pfmFile.I:210
static const LPoint2f & zero()
Returns a zero-length point.
Definition: lpoint2.h:225
void flip(bool flip_x, bool flip_y, bool transpose)
Reverses, transposes, and/or rotates the table in-place according to the specified parameters...
Definition: pfmFile.cxx:1222
xelval * get_alpha_array()
Directly access the underlying PNMImage array of alpha values.
Definition: pnmImage.I:1334
void quick_filter_from(const PfmFile &copy)
Resizes from the given image, with a fixed radius of 0.5.
Definition: pfmFile.cxx:1041
This is an abstract base class that defines the interface for reading image files of various types...
Definition: pnmReader.h:31
This is an abstract base class that defines the interface for writing image files of various types...
Definition: pnmWriter.h:31
bool store_mask(PNMImage &pnmimage) const
Stores 1 or 0 values into the indicated PNMImage, according to has_point() for each pixel...
Definition: pfmFile.cxx:455
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:82
bool load(const PNMImage &pnmimage)
Fills the PfmFile with the data from the indicated PNMImage, converted to floating-point values...
Definition: pfmFile.cxx:312
int pull_spot(const LPoint4f &delta, float xc, float yc, float xr, float yr, float exponent)
Applies delta * t to the point values within radius (xr, yr) distance of (xc, yc).
Definition: pfmFile.cxx:1586
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
virtual int write_data(xel *array, xelval *alpha)
Writes out an entire image all at once, including the header, based on the image data stored in the g...
Definition: pnmWriter.cxx:91
void clear()
Frees all memory allocated for the image, and clears all its parameters (size, color, type, etc).
Definition: pnmImage.cxx:50
void set_alpha(int x, int y, float a)
Sets the alpha component color only at the indicated pixel.
Definition: pnmImage.I:1007
void xform(const LMatrix4f &transform)
Applies the indicated transform matrix to all points in-place.
Definition: pfmFile.cxx:1273
void divide_sub_image(const PfmFile &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1, float pixel_scale=1.0)
Behaves like copy_sub_image(), except the copy pixels are divided into the pixels of the destination...
Definition: pfmFile.cxx:2090
PNMWriter * make_writer(const Filename &filename, PNMFileType *type=NULL) const
Returns a newly-allocated PNMWriter of the suitable type for writing an image to the indicated filena...
This is a four-component point in space.
Definition: lpoint4.h:91
LPoint3f & modify_point(int x, int y)
Returns a modifiable 3-component point value at the indicated point.
Definition: pfmFile.I:233
int get_num_channels() const
Returns the number of channels in the image.
void copy_channel(int to_channel, const PfmFile &other, int from_channel)
Copies just the specified channel values from the indicated PfmFile (which could be same as this PfmF...
Definition: pfmFile.cxx:1477
This is the base class of PNMImage, PNMReader, and PNMWriter.
void clear_no_data_value()
Removes the special value that means "no data" when it appears in the pfm file.
Definition: pfmFile.I:520
void set_row(int row, const LVecBase4f &v)
Replaces the indicated row of the matrix.
Definition: lmatrix.h:1187
bool calc_min_max(LVecBase3f &min_points, LVecBase3f &max_points) const
Calculates the minimum and maximum x, y, and z depth component values, representing the bounding box ...
Definition: pfmFile.cxx:759
This is a two-component point in space.
Definition: lpoint2.h:92
void set_no_data_threshold(const LPoint4f &no_data_value)
Sets the special threshold value.
Definition: pfmFile.cxx:963
void fill_no_data_value()
Fills the table with the current no_data value, so that the table is empty.
Definition: pfmFile.cxx:539
LPoint2f & modify_point2(int x, int y)
Returns a modifiable 2-component point value at the indicated point.
Definition: pfmFile.I:180
bool store(PNMImage &pnmimage) const
Copies the data to the indicated PNMImage, converting to RGB values.
Definition: pfmFile.cxx:387
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:783
void xform_point_general_in_place(LVecBase3f &v) const
The matrix transforms a 3-component point (including translation component), as a fully general opera...
Definition: lmatrix.h:1787
void add_sub_image(const PfmFile &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1, float pixel_scale=1.0)
Behaves like copy_sub_image(), except the copy pixels are added to the pixels of the destination...
Definition: pfmFile.cxx:1955
bool calc_autocrop(int &x_begin, int &x_end, int &y_begin, int &y_end) const
Computes the minimum range of x and y across the PFM file that include all points.
Definition: pfmFile.cxx:800
void set_xel(int x, int y, const LRGBColorf &value)
Changes the RGB color at the indicated pixel.
Definition: pnmImage.I:641
void set_no_data_nan(int num_channels)
Sets the no_data_nan flag.
Definition: pfmFile.cxx:893
void clear()
Eliminates all data in the file.
Definition: pfmFile.cxx:81
This defines a bounding convex hexahedron.
float get_alpha(int x, int y) const
Returns the alpha component color at the indicated pixel.
Definition: pnmImage.I:941
void set_no_data_value(const LPoint4f &no_data_value)
Sets the special value that means "no data" when it appears in the pfm file.
Definition: pfmFile.cxx:931
PNMReader * make_reader(const Filename &filename, PNMFileType *type=NULL, bool report_unknown_type=true) const
Returns a newly-allocated PNMReader of the suitable type for reading from the indicated image filenam...