From 7e645bdca677440baa22db353d158a590269af34 Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 10 May 2013 17:28:34 +0000 Subject: [PATCH] officially support 2-channel PfmFile; load pfm into RGB, not BGR for texture loads --- panda/src/gobj/texture.cxx | 106 +++++++++++- panda/src/pnmimage/pfmFile.cxx | 181 ++++++++++++++++++--- panda/src/pnmimage/pfmFile.h | 8 +- panda/src/pnmimagetypes/pnmFileTypePfm.cxx | 8 + 4 files changed, 271 insertions(+), 32 deletions(-) diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index ddaef99a34..d053d0fda1 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -5912,13 +5912,61 @@ convert_from_pfm(PTA_uchar &image, size_t page_size, int z, nassertv(idx + page_size <= image.size()); PN_float32 *p = (PN_float32 *)&image[idx]; - for (int j = y_size-1; j >= 0; j--) { - for (int i = 0; i < x_size; i++) { - for (int c = 0; c < num_components; ++c) { - *p = pfm.get_channel(i, j, c); - ++p; + switch (num_components) { + case 1: + { + for (int j = y_size-1; j >= 0; j--) { + for (int i = 0; i < x_size; i++) { + p[0] = pfm.get_channel(i, j, 0); + ++p; + } } } + break; + + case 2: + { + for (int j = y_size-1; j >= 0; j--) { + for (int i = 0; i < x_size; i++) { + p[0] = pfm.get_channel(i, j, 0); + p[1] = pfm.get_channel(i, j, 1); + p += 2; + } + } + } + break; + + case 3: + { + // RGB -> BGR + for (int j = y_size-1; j >= 0; j--) { + for (int i = 0; i < x_size; i++) { + p[0] = pfm.get_channel(i, j, 2); + p[1] = pfm.get_channel(i, j, 1); + p[2] = pfm.get_channel(i, j, 0); + p += 3; + } + } + } + break; + + case 4: + { + // RGBA -> BGRA + for (int j = y_size-1; j >= 0; j--) { + for (int i = 0; i < x_size; i++) { + p[0] = pfm.get_channel(i, j, 2); + p[1] = pfm.get_channel(i, j, 1); + p[2] = pfm.get_channel(i, j, 0); + p[3] = pfm.get_channel(i, j, 3); + p += 4; + } + } + } + break; + + default: + nassertv(false); } nassertv((unsigned char *)p == &image[idx] + page_size); @@ -6003,13 +6051,53 @@ convert_to_pfm(PfmFile &pfm, int x_size, int y_size, nassertr(idx + page_size <= image.size(), false); const PN_float32 *p = (const PN_float32 *)&image[idx]; - for (int j = y_size-1; j >= 0; j--) { - for (int i = 0; i < x_size; i++) { - for (int c = 0; c < num_components; ++c) { - pfm.set_channel(i, j, c, *p); + switch (num_components) { + case 1: + for (int j = y_size-1; j >= 0; j--) { + for (int i = 0; i < x_size; i++) { + pfm.set_channel(i, j, 0, p[0]); ++p; } } + break; + + case 2: + for (int j = y_size-1; j >= 0; j--) { + for (int i = 0; i < x_size; i++) { + pfm.set_channel(i, j, 0, p[0]); + pfm.set_channel(i, j, 1, p[1]); + p += 2; + } + } + break; + + case 3: + // BGR -> RGB + for (int j = y_size-1; j >= 0; j--) { + for (int i = 0; i < x_size; i++) { + pfm.set_channel(i, j, 2, p[0]); + pfm.set_channel(i, j, 1, p[1]); + pfm.set_channel(i, j, 0, p[2]); + p += 3; + } + } + break; + + case 4: + // BGRA -> RGBA + for (int j = y_size-1; j >= 0; j--) { + for (int i = 0; i < x_size; i++) { + pfm.set_channel(i, j, 2, p[0]); + pfm.set_channel(i, j, 1, p[1]); + pfm.set_channel(i, j, 0, p[2]); + pfm.set_channel(i, j, 3, p[3]); + p += 4; + } + } + break; + + default: + nassertr(false, false); } nassertr((unsigned char *)p == &image[idx] + page_size, false); diff --git a/panda/src/pnmimage/pfmFile.cxx b/panda/src/pnmimage/pfmFile.cxx index 79bb4a12b1..a9e9642824 100644 --- a/panda/src/pnmimage/pfmFile.cxx +++ b/panda/src/pnmimage/pfmFile.cxx @@ -87,11 +87,16 @@ clear() { //////////////////////////////////////////////////////////////////// // Function: PfmFile::clear // Access: Published -// Description: Resets to an empty table with a specific size. +// Description: Resets to an empty table with a specific size. The +// case of num_channels == 0 is allowed only in the case +// that x_size and y_size are also == 0; and this makes +// an empty (and invalid) PfmFile. //////////////////////////////////////////////////////////////////// void PfmFile:: clear(int x_size, int y_size, int num_channels) { nassertv(x_size >= 0 && y_size >= 0); + nassertv(num_channels > 0 && num_channels <= 4 || (x_size == 0 && y_size == 0 && num_channels == 0)); + _x_size = x_size; _y_size = y_size; _scale = 1.0; @@ -323,25 +328,7 @@ load(const PNMImage &pnmimage) { return false; } - // Get the number of channels, ignoring alpha. - int num_channels; - switch (pnmimage.get_num_channels()) { - case 4: - num_channels = 4; - break; - - case 3: - num_channels = 3; - break; - - case 2: - case 1: - num_channels = 1; - break; - - default: - num_channels = 3; - } + int num_channels = pnmimage.get_num_channels(); clear(pnmimage.get_x_size(), pnmimage.get_y_size(), num_channels); switch (num_channels) { @@ -355,6 +342,18 @@ load(const PNMImage &pnmimage) { } break; + case 2: + { + for (int yi = 0; yi < pnmimage.get_y_size(); ++yi) { + for (int xi = 0; xi < pnmimage.get_x_size(); ++xi) { + PN_float32 *point = &_table[(yi * _x_size + xi) * _num_channels]; + point[0] = pnmimage.get_gray(xi, yi); + point[1] = pnmimage.get_alpha(xi, yi); + } + } + } + break; + case 3: { for (int yi = 0; yi < pnmimage.get_y_size(); ++yi) { @@ -417,11 +416,23 @@ store(PNMImage &pnmimage) const { } break; + case 2: + { + for (int yi = 0; yi < get_y_size(); ++yi) { + for (int xi = 0; xi < get_x_size(); ++xi) { + const LPoint2f &point = get_point2(xi, yi); + pnmimage.set_gray(xi, yi, point[0]); + pnmimage.set_alpha(xi, yi, point[1]); + } + } + } + break; + case 3: { for (int yi = 0; yi < get_y_size(); ++yi) { for (int xi = 0; xi < get_x_size(); ++xi) { - const LPoint3f &point = get_point(xi, yi); + const LPoint3f &point = get_point3(xi, yi); pnmimage.set_xel(xi, yi, point[0], point[1], point[2]); } } @@ -1055,6 +1066,32 @@ quick_filter_from(const PfmFile &from) { } break; + case 2: + { + from_y0 = 0.0; + for (int to_y = 0; to_y < _y_size; ++to_y) { + from_y1 = (to_y + 1.0) * y_scale; + from_y1 = min(from_y1, (PN_float32)orig_y_size); + + from_x0 = 0.0; + for (int to_x = 0; to_x < _x_size; ++to_x) { + from_x1 = (to_x + 1.0) * x_scale; + from_x1 = min(from_x1, (PN_float32)orig_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). + LPoint2f result; + from.box_filter_region(result, from_x0, from_y0, from_x1, from_y1); + new_data.push_back(result[0]); + new_data.push_back(result[1]); + + from_x0 = from_x1; + } + from_y0 = from_y1; + } + } + break; + case 3: { from_y0 = 0.0; @@ -1725,6 +1762,14 @@ compute_sample_point(LPoint3f &result, } break; + case 2: + { + LPoint2f result2; + box_filter_region(result2, x - xr, y - yr, x + xr, y + yr); + result.set(result2[0], result2[1], 0.0); + } + break; + case 3: box_filter_region(result, x - xr, y - yr, x + xr, y + yr); break; @@ -1860,6 +1905,51 @@ box_filter_region(PN_float32 &result, } } +//////////////////////////////////////////////////////////////////// +// 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(LPoint2f &result, + PN_float32 x0, PN_float32 y0, PN_float32 x1, PN_float32 y1) const { + result = LPoint2f::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 @@ -1979,6 +2069,35 @@ box_filter_line(PN_float32 &result, PN_float32 &coverage, } } +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::box_filter_line +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void PfmFile:: +box_filter_line(LPoint2f &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 @@ -2055,6 +2174,24 @@ box_filter_point(PN_float32 &result, PN_float32 &coverage, coverage += contrib; } +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::box_filter_point +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void PfmFile:: +box_filter_point(LPoint2f &result, PN_float32 &coverage, + int x, int y, PN_float32 x_contrib, PN_float32 y_contrib) const { + if (!has_point(x, y)) { + return; + } + const LPoint2f &point = get_point2(x, y); + + PN_float32 contrib = x_contrib * y_contrib; + result += point * contrib; + coverage += contrib; +} + //////////////////////////////////////////////////////////////////// // Function: PfmFile::box_filter_point // Access: Private @@ -2066,7 +2203,7 @@ box_filter_point(LPoint3f &result, PN_float32 &coverage, if (!has_point(x, y)) { return; } - const LPoint3f &point = get_point(x, y); + const LPoint3f &point = get_point3(x, y); PN_float32 contrib = x_contrib * y_contrib; result += point * contrib; diff --git a/panda/src/pnmimage/pfmFile.h b/panda/src/pnmimage/pfmFile.h index 1559fe4dec..b1697481ec 100644 --- a/panda/src/pnmimage/pfmFile.h +++ b/panda/src/pnmimage/pfmFile.h @@ -29,7 +29,7 @@ class PNMWriter; // Class : PfmFile // Description : Defines a pfm file, a 2-d table of floating-point // numbers, either 3-component or 1-component, or with a -// special extension, 4-component. +// special extension, 2- or 4-component. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_PNMIMAGE PfmFile : public PNMImageHeader { PUBLISHED: @@ -147,18 +147,24 @@ private: void box_filter_region(PN_float32 &result, PN_float32 x0, PN_float32 y0, PN_float32 x1, PN_float32 y1) const; + void box_filter_region(LPoint2f &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(LPoint2f &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(LPoint2f &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, diff --git a/panda/src/pnmimagetypes/pnmFileTypePfm.cxx b/panda/src/pnmimagetypes/pnmFileTypePfm.cxx index d7f002c3cc..cc7b5181ff 100644 --- a/panda/src/pnmimagetypes/pnmFileTypePfm.cxx +++ b/panda/src/pnmimagetypes/pnmFileTypePfm.cxx @@ -150,6 +150,10 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) : } else if (magic_number == "Pf") { _num_channels = 1; + } else if (magic_number == "pf2c") { + // Special DRZ extension. + _num_channels = 2; + } else if (magic_number == "pf4c") { // Special DRZ extension. _num_channels = 4; @@ -299,6 +303,10 @@ write_pfm(const PfmFile &pfm) { (*_file) << "Pf\n"; break; + case 2: + (*_file) << "pf2c\n"; + break; + case 3: (*_file) << "PF\n"; break;