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

Reply via email to