Hi!
I sat down for a couple of minutes to extend the pnm plugin with support
for PFMs (a high dynamic range version of the pnm formats which stores
floats - very neat format for CG research ;) ).
Although the patch should support all variants of of the format
properly, I only was able to test it with a couple of color PFMs in
little endian format on a little endian machine. So a lot of variations
are still untested (mostly because I currently lack test files for them).
I'd like to know what you think of this patch.
Regards,
Gregor
diff --git a/src/pnm.imageio/pnminput.cpp b/src/pnm.imageio/pnminput.cpp
index 9d777fd..161a20c 100644
--- a/src/pnm.imageio/pnminput.cpp
+++ b/src/pnm.imageio/pnminput.cpp
@@ -34,6 +34,7 @@
#include "export.h"
#include "filesystem.h"
+#include "fmath.h"
#include "imageio.h"
OIIO_PLUGIN_NAMESPACE_BEGIN
@@ -49,10 +50,16 @@ public:
virtual bool read_native_scanline (int y, int z, void *data);
private:
+ enum PNMType {
+ P1, P2, P3, P4, P5, P6, Pf, PF
+ };
+
std::ifstream m_file;
std::string m_current_line; ///< Buffer the image pixels
const char * m_pos;
- unsigned int m_pnm_type, m_max_val;
+ PNMType m_pnm_type;
+ unsigned int m_max_val;
+ float m_scaling_factor;
bool read_file_scanline (void * data);
bool read_file_header ();
@@ -68,7 +75,7 @@ OIIO_PLUGIN_EXPORTS_BEGIN
OIIO_EXPORT int pnm_imageio_version = OIIO_PLUGIN_VERSION;
OIIO_EXPORT const char* pnm_input_extensions[] = {
- "ppm","pgm","pbm","pnm", NULL
+ "ppm","pgm","pbm","pnm", "pfm", NULL
};
OIIO_PLUGIN_EXPORTS_END
@@ -192,6 +199,21 @@ unpack (const unsigned char * read, unsigned char * write, imagesize_t size)
}
}
+inline void
+unpack_floats(const unsigned char * read, float * write, imagesize_t numsamples, float scaling_factor)
+{
+ float *read_floats = (float *)read;
+
+ if((scaling_factor < 0 && bigendian()) || (scaling_factor > 0 && littleendian())) {
+ swap_endian(read_floats, numsamples);
+ }
+
+ float absfactor = fabs(scaling_factor);
+ for(imagesize_t i = 0; i < numsamples; i++) {
+ write[i] = absfactor * read_floats[i];
+ }
+}
+
template <class T>
@@ -230,10 +252,12 @@ PNMInput::read_file_scanline (void * data)
return false;
int nsamples = m_spec.width * m_spec.nchannels;
- if (m_pnm_type >= 4 && m_pnm_type <= 6){
+ if ((m_pnm_type >= P4 && m_pnm_type <= P6) || m_pnm_type == PF || m_pnm_type == Pf){
int numbytes;
- if (m_pnm_type == 4)
+ if (m_pnm_type == P4)
numbytes = (m_spec.width + 7) / 8;
+ else if(m_pnm_type == PF || m_pnm_type == Pf)
+ numbytes = m_spec.nchannels * 4 * m_spec.width;
else
numbytes = m_spec.scanline_bytes();
buf.resize (numbytes);
@@ -244,13 +268,13 @@ PNMInput::read_file_scanline (void * data)
switch (m_pnm_type) {
//Ascii
- case 1:
+ case P1:
good &= ascii_to_raw (m_file, m_current_line, m_pos, (unsigned char *) data,
nsamples, (unsigned char)m_max_val);
invert ((unsigned char *)data, (unsigned char *)data, nsamples);
break;
- case 2:
- case 3:
+ case P2:
+ case P3:
if (m_max_val > std::numeric_limits<unsigned char>::max())
good &= ascii_to_raw (m_file, m_current_line, m_pos, (unsigned short *) data,
nsamples, (unsigned short)m_max_val);
@@ -259,11 +283,11 @@ PNMInput::read_file_scanline (void * data)
nsamples, (unsigned char)m_max_val);
break;
//Raw
- case 4:
+ case P4:
unpack (&buf[0], (unsigned char *)data, nsamples);
break;
- case 5:
- case 6:
+ case P5:
+ case P6:
if (m_max_val > std::numeric_limits<unsigned char>::max())
raw_to_raw ((unsigned short *)&buf[0], (unsigned short *) data,
nsamples, (unsigned short)m_max_val);
@@ -271,6 +295,11 @@ PNMInput::read_file_scanline (void * data)
raw_to_raw ((unsigned char *)&buf[0], (unsigned char *) data,
nsamples, (unsigned char)m_max_val);
break;
+ //Floating point
+ case Pf:
+ case PF:
+ unpack_floats(&buf[0], (float *)data, nsamples, m_scaling_factor);
+ break;
default:
return false;
}
@@ -294,51 +323,105 @@ PNMInput::read_file_header ()
char c;
if (!m_file.is_open())
return false;
-
- m_file >> c >> m_pnm_type;
-
+
//MagicNumber
+ m_file >> c;
if (c != 'P')
return false;
- if (!(m_pnm_type >= 1 && m_pnm_type <= 6))
+
+ m_file >> c;
+ switch(c) {
+ case '1':
+ m_pnm_type = P1;
+ break;
+ case '2':
+ m_pnm_type = P2;
+ break;
+ case '3':
+ m_pnm_type = P3;
+ break;
+ case '4':
+ m_pnm_type = P4;
+ break;
+ case '5':
+ m_pnm_type = P5;
+ break;
+ case '6':
+ m_pnm_type = P6;
+ break;
+ case 'f':
+ m_pnm_type = Pf;
+ break;
+ case 'F':
+ m_pnm_type = PF;
+ break;
+ default:
return false;
-
+ }
+
//Size
if (!read_int (m_file, width))
return false;
if (!read_int (m_file, height))
return false;
-
- //Max Val
- if (m_pnm_type != 1 && m_pnm_type != 4) {
- if (!read_int (m_file, m_max_val))
+
+ if(m_pnm_type != PF && m_pnm_type != Pf) {
+ //Max Val
+ if (m_pnm_type != P1 && m_pnm_type != P4) {
+ if (!read_int (m_file, m_max_val))
+ return false;
+ } else
+ m_max_val = 1;
+
+ //Space before content
+ if (!(isspace (m_file.get()) && m_file.good()))
return false;
- } else
- m_max_val = 1;
-
- //Space before content
- if (!(isspace (m_file.get()) && m_file.good()))
- return false;
- if (m_pnm_type == 3 || m_pnm_type == 6)
- m_spec = ImageSpec (width, height, 3,
- (m_max_val > 255) ? TypeDesc::UINT16 : TypeDesc::UINT8);
- else
- m_spec = ImageSpec (width, height, 1,
- (m_max_val > 255) ? TypeDesc::UINT16 : TypeDesc::UINT8);
+ if (m_pnm_type == P3 || m_pnm_type == P6)
+ m_spec = ImageSpec (width, height, 3,
+ (m_max_val > 255) ? TypeDesc::UINT16 : TypeDesc::UINT8);
+ else
+ m_spec = ImageSpec (width, height, 1,
+ (m_max_val > 255) ? TypeDesc::UINT16 : TypeDesc::UINT8);
- if (m_spec.nchannels == 1)
- m_spec.channelnames[0] = "I";
- else
- m_spec.default_channel_names();
+ if (m_spec.nchannels == 1)
+ m_spec.channelnames[0] = "I";
+ else
+ m_spec.default_channel_names();
- if (m_pnm_type >= 1 && m_pnm_type <= 3)
- m_spec.attribute ("pnm:binary", 0);
- else
- m_spec.attribute ("pnm:binary", 1);
+ if (m_pnm_type >= P1 && m_pnm_type <= P3)
+ m_spec.attribute ("pnm:binary", 0);
+ else
+ m_spec.attribute ("pnm:binary", 1);
- m_spec.attribute ("oiio:BitsPerSample", ceilf (logf (m_max_val + 1)/logf (2)));
- return true;
+ m_spec.attribute ("oiio:BitsPerSample", ceilf (logf (m_max_val + 1)/logf (2)));
+ return true;
+ } else {
+ //Read scaling factor
+ if(!read_int(m_file, m_scaling_factor)) {
+ return false;
+ }
+
+ //Space before content
+ if (!(isspace (m_file.get()) && m_file.good()))
+ return false;
+
+ if(m_pnm_type == PF) {
+ m_spec = ImageSpec(width, height, 3, TypeDesc::FLOAT);
+ m_spec.default_channel_names();
+ } else {
+ m_spec = ImageSpec(width, height, 1, TypeDesc::FLOAT);
+ m_spec.channelnames[0] = "I";
+ }
+
+ if(m_scaling_factor < 0) {
+ m_spec.attribute("pnm:bigendian", 0);
+ } else {
+ m_spec.attribute("pnm:bigendian", 1);
+ }
+
+ return true;
+ }
}
catch (const std::exception &e) {
error ("PNM exception: %s", e.what());
_______________________________________________
Oiio-dev mailing list
[email protected]
http://lists.openimageio.org/listinfo.cgi/oiio-dev-openimageio.org