diff --git a/panda/src/grutil/pfmFile.I b/panda/src/grutil/pfmFile.I index b785af1df4..2c0674475c 100755 --- a/panda/src/grutil/pfmFile.I +++ b/panda/src/grutil/pfmFile.I @@ -20,7 +20,7 @@ //////////////////////////////////////////////////////////////////// INLINE bool PfmFile:: is_valid() const { - return _num_channels != 0 && (_x_size * _y_size * _num_channels == (int)_table.size()); + return _num_channels != 0 && (_x_size * _y_size * _num_channels <= (int)_table.size()); } //////////////////////////////////////////////////////////////////// @@ -82,6 +82,33 @@ has_point(int x, int y) const { return _has_point(this, x, y); } +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::get_point1 +// Access: Published +// Description: Returns the 1-component point value at the indicated +// point. +//////////////////////////////////////////////////////////////////// +INLINE PN_float32 PfmFile:: +get_point1(int x, int y) const { + nassertr(x >= 0 && x < _x_size, 0.0); + nassertr(y >= 0 && y < _y_size, 0.0); + return _table[(y * _x_size + x) * _num_channels]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::set_point1 +// Access: Published +// Description: Replaces the 1-component point value at the indicated +// point. +//////////////////////////////////////////////////////////////////// +INLINE void PfmFile:: +set_point1(int x, int y, PN_float32 point) { + nassertv(!cnan(point)); + nassertv(x >= 0 && x < _x_size); + nassertv(y >= 0 && y < _y_size); + _table[(y * _x_size + x) * _num_channels] = point; +} + //////////////////////////////////////////////////////////////////// // Function: PfmFile::get_point // Access: Published diff --git a/panda/src/grutil/pfmFile.cxx b/panda/src/grutil/pfmFile.cxx index 310fe1f6b7..b126dd8591 100755 --- a/panda/src/grutil/pfmFile.cxx +++ b/panda/src/grutil/pfmFile.cxx @@ -117,7 +117,12 @@ clear(int x_size, int y_size, int num_channels) { _table.clear(); int size = _x_size * _y_size * _num_channels; - _table.insert(_table.end(), size, (PN_float32)0.0); + + // We allocate a little bit bigger to allow safe overflow: you can + // call get_point3() or get_point4() on the last point of a 1- or + // 3-channel image. + _table.insert(_table.end(), size + 4, (PN_float32)0.0); + clear_no_data_value(); } @@ -243,7 +248,11 @@ read(istream &in, const Filename &fullpath, const string &magic_number) { // So far, so good. Now read the data. int size = _x_size * _y_size * _num_channels; - _table.insert(_table.end(), size, (PN_float32)0.0); + + // We allocate a little bit bigger to allow safe overflow: you can + // call get_point3() or get_point4() on the last point of a 1- or + // 3-channel image. + _table.insert(_table.end(), size + 4, (PN_float32)0.0); in.read((char *)&_table[0], sizeof(PN_float32) * size); if (in.fail() && !in.eof()) { @@ -784,27 +793,89 @@ resize(int new_x_size, int new_y_size) { y_scale = (PN_float32)(_y_size - 1) / (PN_float32)(new_y_size - 1); } - from_y0 = 0.0; - for (int to_y = 0; to_y < new_y_size; ++to_y) { - from_y1 = (to_y + 0.5) * y_scale; - from_y1 = min(from_y1, (PN_float32) _y_size); - - from_x0 = 0.0; - for (int to_x = 0; to_x < new_x_size; ++to_x) { - from_x1 = (to_x + 0.5) * x_scale; - from_x1 = min(from_x1, (PN_float32) _x_size); - - // Now the box from (from_x0, from_y0) - (from_x1, from_y1) - // but not including (from_x1, from_y1) maps to the pixel (to_x, to_y). - LPoint4f result; - box_filter_region(result, from_x0, from_y0, from_x1, from_y1); - for (int ci = 0; ci < _num_channels; ++ci) { - new_data.push_back(result[ci]); + switch (_num_channels) { + case 1: + { + from_y0 = 0.0; + for (int to_y = 0; to_y < new_y_size; ++to_y) { + from_y1 = (to_y + 0.5) * y_scale; + from_y1 = min(from_y1, (PN_float32) _y_size); + + from_x0 = 0.0; + for (int to_x = 0; to_x < new_x_size; ++to_x) { + from_x1 = (to_x + 0.5) * x_scale; + from_x1 = min(from_x1, (PN_float32) _x_size); + + // Now the box from (from_x0, from_y0) - (from_x1, from_y1) + // but not including (from_x1, from_y1) maps to the pixel (to_x, to_y). + PN_float32 result; + box_filter_region(result, from_x0, from_y0, from_x1, from_y1); + new_data.push_back(result); + + from_x0 = from_x1; + } + from_y0 = from_y1; } - - from_x0 = from_x1; } - from_y0 = from_y1; + break; + + case 3: + { + from_y0 = 0.0; + for (int to_y = 0; to_y < new_y_size; ++to_y) { + from_y1 = (to_y + 0.5) * y_scale; + from_y1 = min(from_y1, (PN_float32) _y_size); + + from_x0 = 0.0; + for (int to_x = 0; to_x < new_x_size; ++to_x) { + from_x1 = (to_x + 0.5) * x_scale; + from_x1 = min(from_x1, (PN_float32) _x_size); + + // Now the box from (from_x0, from_y0) - (from_x1, from_y1) + // but not including (from_x1, from_y1) maps to the pixel (to_x, to_y). + LPoint3f result; + box_filter_region(result, from_x0, from_y0, from_x1, from_y1); + new_data.push_back(result[0]); + new_data.push_back(result[1]); + new_data.push_back(result[2]); + + from_x0 = from_x1; + } + from_y0 = from_y1; + } + } + break; + + case 4: + { + from_y0 = 0.0; + for (int to_y = 0; to_y < new_y_size; ++to_y) { + from_y1 = (to_y + 0.5) * y_scale; + from_y1 = min(from_y1, (PN_float32) _y_size); + + from_x0 = 0.0; + for (int to_x = 0; to_x < new_x_size; ++to_x) { + from_x1 = (to_x + 0.5) * x_scale; + from_x1 = min(from_x1, (PN_float32) _x_size); + + // Now the box from (from_x0, from_y0) - (from_x1, from_y1) + // but not including (from_x1, from_y1) maps to the pixel (to_x, to_y). + LPoint4f result; + box_filter_region(result, from_x0, from_y0, from_x1, from_y1); + new_data.push_back(result[0]); + new_data.push_back(result[1]); + new_data.push_back(result[2]); + new_data.push_back(result[3]); + + from_x0 = from_x1; + } + from_y0 = from_y1; + } + } + break; + + default: + nassertv(false); } nassertv(new_data.size() == new_x_size * new_y_size * _num_channels); @@ -832,7 +903,10 @@ reverse_rows() { _table.begin() + start, _table.begin() + start + row_size); } - nassertv(reversed.size() == _table.size()); + nassertv(reversed.size() <= _table.size()); + // Also add in the extra buffer at the end. + reversed.insert(reversed.end(), _table.size() - reversed.size(), (PN_float32)0.0); + _table.swap(reversed); } @@ -987,7 +1061,11 @@ apply_crop(int x_begin, int x_end, int y_begin, int y_end) { int new_y_size = y_end - y_begin; Table new_table; int new_size = new_x_size * new_y_size * _num_channels; - new_table.insert(new_table.end(), new_size, (PN_float32)0.0); + + // We allocate a little bit bigger to allow safe overflow: you can + // call get_point3() or get_point4() on the last point of a 1- or + // 3-channel image. + new_table.insert(new_table.end(), new_size + 4, (PN_float32)0.0); for (int yi = 0; yi < new_y_size; ++yi) { memcpy(&new_table[(yi * new_x_size) * _num_channels], @@ -1184,9 +1262,31 @@ compute_sample_point(LPoint3f &result, y *= _y_size; PN_float32 xr = sample_radius * _x_size; PN_float32 yr = sample_radius * _y_size; - LPoint4f result4; - box_filter_region(result4, x - xr, y - yr, x + xr, y + yr); - result.set(result4[0], result4[1], result4[2]); + + switch (_num_channels) { + case 1: + { + PN_float32 result1; + box_filter_region(result1, x - xr, y - yr, x + xr, y + yr); + result.set(result1, 0.0, 0.0); + } + break; + + case 3: + box_filter_region(result, x - xr, y - yr, x + xr, y + yr); + break; + + case 4: + { + LPoint4f result4; + box_filter_region(result4, x - xr, y - yr, x + xr, y + yr); + result.set(result4[0], result4[1], result4[2]); + } + break; + + default: + nassertv(false); + } } @@ -1525,6 +1625,96 @@ make_vis_mesh_geom(GeomNode *gnode, bool inverted) const { } +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::box_filter_region +// Access: Private +// Description: Averages all the points in the rectangle from x0 +// .. y0 to x1 .. y1 into result. The region may be +// defined by floating-point boundaries; the result will +// be weighted by the degree of coverage of each +// included point. +//////////////////////////////////////////////////////////////////// +void PfmFile:: +box_filter_region(PN_float32 &result, + PN_float32 x0, PN_float32 y0, PN_float32 x1, PN_float32 y1) const { + result = 0.0; + PN_float32 coverage = 0.0; + + if (x1 < x0 || y1 < y0) { + return; + } + nassertv(y0 >= 0.0 && y1 >= 0.0); + + int y = (int)y0; + // Get the first (partial) row + box_filter_line(result, coverage, x0, y, x1, (PN_float32)(y+1)-y0); + + int y_last = (int)y1; + if (y < y_last) { + y++; + while (y < y_last) { + // Get each consecutive (complete) row + box_filter_line(result, coverage, x0, y, x1, 1.0); + y++; + } + + // Get the final (partial) row + PN_float32 y_contrib = y1 - (PN_float32)y_last; + if (y_contrib > 0.0001) { + box_filter_line(result, coverage, x0, y, x1, y_contrib); + } + } + + if (coverage != 0.0) { + result /= coverage; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::box_filter_region +// Access: Private +// Description: Averages all the points in the rectangle from x0 +// .. y0 to x1 .. y1 into result. The region may be +// defined by floating-point boundaries; the result will +// be weighted by the degree of coverage of each +// included point. +//////////////////////////////////////////////////////////////////// +void PfmFile:: +box_filter_region(LPoint3f &result, + PN_float32 x0, PN_float32 y0, PN_float32 x1, PN_float32 y1) const { + result = LPoint3f::zero(); + PN_float32 coverage = 0.0; + + if (x1 < x0 || y1 < y0) { + return; + } + nassertv(y0 >= 0.0 && y1 >= 0.0); + + int y = (int)y0; + // Get the first (partial) row + box_filter_line(result, coverage, x0, y, x1, (PN_float32)(y+1)-y0); + + int y_last = (int)y1; + if (y < y_last) { + y++; + while (y < y_last) { + // Get each consecutive (complete) row + box_filter_line(result, coverage, x0, y, x1, 1.0); + y++; + } + + // Get the final (partial) row + PN_float32 y_contrib = y1 - (PN_float32)y_last; + if (y_contrib > 0.0001) { + box_filter_line(result, coverage, x0, y, x1, y_contrib); + } + } + + if (coverage != 0.0) { + result /= coverage; + } +} + //////////////////////////////////////////////////////////////////// // Function: PfmFile::box_filter_region // Access: Private @@ -1570,6 +1760,64 @@ box_filter_region(LPoint4f &result, } } +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::box_filter_line +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void PfmFile:: +box_filter_line(PN_float32 &result, PN_float32 &coverage, + PN_float32 x0, int y, PN_float32 x1, PN_float32 y_contrib) const { + int x = (int)x0; + // Get the first (partial) xel + box_filter_point(result, coverage, x, y, (PN_float32)(x+1)-x0, y_contrib); + + int x_last = (int)x1; + if (x < x_last) { + x++; + while (x < x_last) { + // Get each consecutive (complete) xel + box_filter_point(result, coverage, x, y, 1.0, y_contrib); + x++; + } + + // Get the final (partial) xel + PN_float32 x_contrib = x1 - (PN_float32)x_last; + if (x_contrib > 0.0001) { + box_filter_point(result, coverage, x, y, x_contrib, y_contrib); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::box_filter_line +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void PfmFile:: +box_filter_line(LPoint3f &result, PN_float32 &coverage, + PN_float32 x0, int y, PN_float32 x1, PN_float32 y_contrib) const { + int x = (int)x0; + // Get the first (partial) xel + box_filter_point(result, coverage, x, y, (PN_float32)(x+1)-x0, y_contrib); + + int x_last = (int)x1; + if (x < x_last) { + x++; + while (x < x_last) { + // Get each consecutive (complete) xel + box_filter_point(result, coverage, x, y, 1.0, y_contrib); + x++; + } + + // Get the final (partial) xel + PN_float32 x_contrib = x1 - (PN_float32)x_last; + if (x_contrib > 0.0001) { + box_filter_point(result, coverage, x, y, x_contrib, y_contrib); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: PfmFile::box_filter_line // Access: Private @@ -1599,6 +1847,42 @@ box_filter_line(LPoint4f &result, PN_float32 &coverage, } } +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::box_filter_point +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void PfmFile:: +box_filter_point(PN_float32 &result, PN_float32 &coverage, + int x, int y, PN_float32 x_contrib, PN_float32 y_contrib) const { + if (!has_point(x, y)) { + return; + } + PN_float32 point = get_point1(x, y); + + PN_float32 contrib = x_contrib * y_contrib; + result += point * contrib; + coverage += contrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::box_filter_point +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void PfmFile:: +box_filter_point(LPoint3f &result, PN_float32 &coverage, + int x, int y, PN_float32 x_contrib, PN_float32 y_contrib) const { + if (!has_point(x, y)) { + return; + } + const LPoint3f &point = get_point(x, y); + + PN_float32 contrib = x_contrib * y_contrib; + result += point * contrib; + coverage += contrib; +} + //////////////////////////////////////////////////////////////////// // Function: PfmFile::box_filter_point // Access: Private diff --git a/panda/src/grutil/pfmFile.h b/panda/src/grutil/pfmFile.h index 5777d91124..42bbff179d 100755 --- a/panda/src/grutil/pfmFile.h +++ b/panda/src/grutil/pfmFile.h @@ -57,6 +57,8 @@ PUBLISHED: INLINE int get_num_channels() const; INLINE bool has_point(int x, int y) const; + INLINE PN_float32 get_point1(int x, int y) const; + INLINE void set_point1(int x, int y, PN_float32 point); INLINE const LPoint3f &get_point(int x, int y) const; INLINE void set_point(int x, int y, const LVecBase3f &point); INLINE void set_point(int x, int y, const LVecBase3d &point); @@ -117,10 +119,22 @@ PUBLISHED: private: void make_vis_mesh_geom(GeomNode *gnode, bool inverted) const; + void box_filter_region(PN_float32 &result, + PN_float32 x0, PN_float32 y0, PN_float32 x1, PN_float32 y1) const; + void box_filter_region(LPoint3f &result, + PN_float32 x0, PN_float32 y0, PN_float32 x1, PN_float32 y1) const; void box_filter_region(LPoint4f &result, PN_float32 x0, PN_float32 y0, PN_float32 x1, PN_float32 y1) const; + void box_filter_line(PN_float32 &result, PN_float32 &coverage, + PN_float32 x0, int y, PN_float32 x1, PN_float32 y_contrib) const; + void box_filter_line(LPoint3f &result, PN_float32 &coverage, + PN_float32 x0, int y, PN_float32 x1, PN_float32 y_contrib) const; void box_filter_line(LPoint4f &result, PN_float32 &coverage, PN_float32 x0, int y, PN_float32 x1, PN_float32 y_contrib) const; + void box_filter_point(PN_float32 &result, PN_float32 &coverage, + int x, int y, PN_float32 x_contrib, PN_float32 y_contrib) const; + void box_filter_point(LPoint3f &result, PN_float32 &coverage, + int x, int y, PN_float32 x_contrib, PN_float32 y_contrib) const; void box_filter_point(LPoint4f &result, PN_float32 &coverage, int x, int y, PN_float32 x_contrib, PN_float32 y_contrib) const;