Kaynağa Gözat

integrate PfmFile with PNMImage, Texture

David Rose 13 yıl önce
ebeveyn
işleme
7ba693137c

+ 268 - 58
panda/src/gobj/texture.cxx

@@ -28,6 +28,8 @@
 #include "string_utils.h"
 #include "preparedGraphicsObjects.h"
 #include "pnmImage.h"
+#include "pnmReader.h"
+#include "pfmFile.h"
 #include "virtualFileSystem.h"
 #include "datagramInputFile.h"
 #include "datagramOutputFile.h"
@@ -2913,12 +2915,40 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa
   }
 
   PNMImage image;
-  if (header_only || textures_header_only) {
-    if (!image.read_header(fullpath)) {
-      gobj_cat.error()
-        << "Texture::read() - couldn't read: " << fullpath << endl;
-      return false;
+  PfmFile pfm;
+  PNMReader *image_reader = image.make_reader(fullpath, NULL, false);
+  if (image_reader == NULL) {
+    gobj_cat.error()
+      << "Texture::read() - couldn't read: " << fullpath << endl;
+    return false;
+  }
+  image.copy_header_from(*image_reader);
+
+  // If it's a single-channel floating-point image file, read it by
+  // default into a floating-point texture.  (Multi-channel image
+  // files are ready as integers by default, since current graphics
+  // API's don't support multi-channel float textures.)
+  bool read_floating_point;
+  int texture_load_type = (options.get_texture_flags() & (LoaderOptions::TF_integer | LoaderOptions::TF_float));
+  switch (texture_load_type) {
+  case LoaderOptions::TF_integer:
+    read_floating_point = false;
+    break;
+
+  case LoaderOptions::TF_float:
+    read_floating_point = true;
+    break;
+
+  default:
+    // Neither TF_integer nor TF_float was specified; determine which
+    // way the texture wants to be loaded.
+    read_floating_point = (image_reader->is_floating_point() && image_reader->get_num_channels() == 1);
+    if (!alpha_fullpath.empty()) {
+      read_floating_point = false;
     }
+  }
+
+  if (header_only || textures_header_only) {
     int x_size = image.get_x_size();
     int y_size = image.get_y_size();
     if (z == 0 && n == 0) {
@@ -2938,20 +2968,19 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa
       y_size = image.get_read_y_size();
     }
 
-    image = PNMImage(x_size, y_size, image.get_num_channels(),
-                     image.get_maxval(), image.get_type());
-    image.fill(0.2, 0.3, 1.0);
-    if (image.has_alpha()) {
-      image.alpha_fill(1.0);
+    if (read_floating_point) {
+      pfm.clear(x_size, y_size, image.get_num_channels());
+    } else {
+      image = PNMImage(x_size, y_size, image.get_num_channels(),
+                       image.get_maxval(), image.get_type());
+      image.fill(0.2, 0.3, 1.0);
+      if (image.has_alpha()) {
+        image.alpha_fill(1.0);
+      }
     }
+    delete image_reader;
 
   } else {
-    if (!image.read_header(fullpath, NULL, false)) {
-      gobj_cat.error()
-        << "Texture::read() - couldn't read: " << fullpath << endl;
-      return false;
-    }
-
     if (z == 0 && n == 0) {
       cdata->_orig_file_x_size = image.get_x_size();
       cdata->_orig_file_y_size = image.get_y_size();
@@ -2970,7 +2999,14 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa
         << "\n";
     }
 
-    if (!image.read(fullpath, NULL, false)) {
+    bool success;
+    if (read_floating_point) {
+      success = pfm.read(image_reader);
+    } else {
+      success = image.read(image_reader);
+    }
+
+    if (!success) {
       gobj_cat.error()
         << "Texture::read() - couldn't read: " << fullpath << endl;
       return false;
@@ -2980,16 +3016,19 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa
 
   PNMImage alpha_image;
   if (!alpha_fullpath.empty()) {
+    PNMReader *alpha_image_reader = alpha_image.make_reader(alpha_fullpath, NULL, false);
+    if (alpha_image_reader == NULL) {
+      gobj_cat.error()
+        << "Texture::read() - couldn't read: " << alpha_fullpath << endl;
+      return false;
+    }
+    alpha_image.copy_header_from(*alpha_image_reader);
+
     if (record != (BamCacheRecord *)NULL) {
       record->add_dependent_file(alpha_fullpath);
     }
 
     if (header_only || textures_header_only) {
-      if (!alpha_image.read_header(alpha_fullpath)) {
-        gobj_cat.error()
-          << "Texture::read() - couldn't read: " << alpha_fullpath << endl;
-        return false;
-      }
       int x_size = image.get_x_size();
       int y_size = image.get_y_size();
       alpha_image = PNMImage(x_size, y_size, alpha_image.get_num_channels(),
@@ -2998,14 +3037,9 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa
       if (alpha_image.has_alpha()) {
         alpha_image.alpha_fill(1.0);
       }
+      delete alpha_image_reader;
 
     } else {
-      if (!alpha_image.read_header(alpha_fullpath, NULL, true)) {
-        gobj_cat.error()
-          << "Texture::read() - couldn't read (alpha): " << alpha_fullpath << endl;
-        return false;
-      }
-
       if (image.get_x_size() != alpha_image.get_x_size() ||
           image.get_y_size() != alpha_image.get_y_size()) {
         gobj_cat.info()
@@ -3016,7 +3050,7 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa
         alpha_image.set_read_size(image.get_x_size(), image.get_y_size());
       }
 
-      if (!alpha_image.read(alpha_fullpath, NULL, true)) {
+      if (!alpha_image.read(alpha_image_reader)) {
         gobj_cat.error()
           << "Texture::read() - couldn't read (alpha): " << alpha_fullpath << endl;
         return false;
@@ -3106,28 +3140,34 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa
     }
   }
 
-  // Now see if we want to pad the image within a larger power-of-2
-  // image.
-  int pad_x_size = 0;
-  int pad_y_size = 0;
-  if (do_get_auto_texture_scale(cdata) == ATS_pad) {
-    int new_x_size = image.get_x_size();
-    int new_y_size = image.get_y_size();
-    if (do_adjust_this_size(cdata, new_x_size, new_y_size, fullpath.get_basename(), true)) {
-      pad_x_size = new_x_size - image.get_x_size();
-      pad_y_size = new_y_size - image.get_y_size();
-      PNMImage new_image(new_x_size, new_y_size, image.get_num_channels(),
-                         image.get_maxval());
-      new_image.copy_sub_image(image, 0, new_y_size - image.get_y_size());
-      image.take_from(new_image);
+  if (read_floating_point) {
+    if (!do_load_one(cdata, pfm, fullpath.get_basename(), z, n, options)) {
+      return false;
+    }
+  } else {
+    // Now see if we want to pad the image within a larger power-of-2
+    // image.
+    int pad_x_size = 0;
+    int pad_y_size = 0;
+    if (do_get_auto_texture_scale(cdata) == ATS_pad) {
+      int new_x_size = image.get_x_size();
+      int new_y_size = image.get_y_size();
+      if (do_adjust_this_size(cdata, new_x_size, new_y_size, fullpath.get_basename(), true)) {
+        pad_x_size = new_x_size - image.get_x_size();
+        pad_y_size = new_y_size - image.get_y_size();
+        PNMImage new_image(new_x_size, new_y_size, image.get_num_channels(),
+                           image.get_maxval());
+        new_image.copy_sub_image(image, 0, new_y_size - image.get_y_size());
+        image.take_from(new_image);
+      }
+    }
+    
+    if (!do_load_one(cdata, image, fullpath.get_basename(), z, n, options)) {
+      return false;
     }
-  }
 
-  if (!do_load_one(cdata, image, fullpath.get_basename(), z, n, options)) {
-    return false;
+    do_set_pad_size(cdata, pad_x_size, pad_y_size, 0);
   }
-
-  do_set_pad_size(cdata, pad_x_size, pad_y_size, 0);
   return true;
 }
 
@@ -3205,6 +3245,74 @@ do_load_one(CData *cdata, const PNMImage &pnmimage, const string &name, int z, i
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::do_load_one
+//       Access: Protected, Virtual
+//  Description: Internal method to load a single page or mipmap
+//               level.
+////////////////////////////////////////////////////////////////////
+bool Texture::
+do_load_one(CData *cdata, const PfmFile &pfm, const string &name, int z, int n,
+            const LoaderOptions &options) {
+  if (cdata->_ram_images.size() <= 1 && n == 0) {
+    // A special case for mipmap level 0.  When we load mipmap level
+    // 0, unless we already have mipmap levels, it determines the
+    // image properties like size and number of components.
+    if (!do_reconsider_z_size(cdata, z, options)) {
+      return false;
+    }
+    nassertr(z >= 0 && z < cdata->_z_size * cdata->_num_views, false);
+
+    if (z == 0) {
+      ComponentType component_type = T_float;
+      if (!do_reconsider_image_properties(cdata, pfm.get_x_size(), pfm.get_y_size(),
+                                          pfm.get_num_channels(), component_type,
+                                          z, options)) {
+        return false;
+      }
+    }
+
+    do_modify_ram_image(cdata);
+    cdata->_loaded_from_image = true;
+  }
+
+  do_modify_ram_mipmap_image(cdata, n);
+
+  // Ensure the PfmFile is an appropriate size.
+  int x_size = do_get_expected_mipmap_x_size(cdata, n);
+  int y_size = do_get_expected_mipmap_y_size(cdata, n);
+  if (pfm.get_x_size() != x_size ||
+      pfm.get_y_size() != y_size) {
+    gobj_cat.info()
+      << "Automatically rescaling " << name;
+    if (n != 0) {
+      gobj_cat.info(false)
+        << " mipmap level " << n;
+    }
+    gobj_cat.info(false)
+      << " from " << pfm.get_x_size() << " by "
+      << pfm.get_y_size() << " to " << x_size << " by "
+      << y_size << "\n";
+
+    PfmFile scaled(pfm);
+    scaled.resize(x_size, y_size);
+    Thread::consider_yield();
+
+    convert_from_pfm(cdata->_ram_images[n]._image,
+                     do_get_expected_ram_mipmap_page_size(cdata, n), z,
+                     scaled, cdata->_num_components, cdata->_component_width);
+  } else {
+    // Now copy the pixel data from the PfmFile into our internal
+    // cdata->_image component.
+    convert_from_pfm(cdata->_ram_images[n]._image,
+                     do_get_expected_ram_mipmap_page_size(cdata, n), z,
+                     pfm, cdata->_num_components, cdata->_component_width);
+  }
+  Thread::consider_yield();
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::do_read_txo_file
 //       Access: Protected
@@ -3691,16 +3799,29 @@ do_write_one(CData *cdata, const Filename &fullpath, int z, int n) {
 
   nassertr(cdata->_ram_image_compression == CM_off, false);
 
-  PNMImage pnmimage;
-  if (!do_store_one(cdata, pnmimage, z, n)) {
-    return false;
+  bool success;
+  if (cdata->_component_type == T_float) {
+    // Writing a floating-point texture.
+    PfmFile pfm;
+    if (!do_store_one(cdata, pfm, z, n)) {
+      return false;
+    }
+    success = pfm.write(fullpath);
+  } else {
+    // Writing a normal, integer texture.
+    PNMImage pnmimage;
+    if (!do_store_one(cdata, pnmimage, z, n)) {
+      return false;
+    }
+    success = pnmimage.write(fullpath);
   }
 
-  if (!pnmimage.write(fullpath)) {
+  if (!success) {
     gobj_cat.error()
       << "Texture::write() - couldn't write: " << fullpath << endl;
     return false;
   }
+
   return true;
 }
 
@@ -3730,6 +3851,32 @@ do_store_one(CData *cdata, PNMImage &pnmimage, int z, int n) {
                              do_get_ram_mipmap_page_size(cdata, n), z);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::do_store_one
+//       Access: Protected
+//  Description: Internal method to copy a page and/or mipmap level to
+//               a PfmFile.
+////////////////////////////////////////////////////////////////////
+bool Texture::
+do_store_one(CData *cdata, PfmFile &pfm, int z, int n) {
+  // First, reload the ram image if necessary.
+  do_get_uncompressed_ram_image(cdata);
+
+  if (!do_has_ram_mipmap_image(cdata, n)) {
+    return false;
+  }
+
+  nassertr(z >= 0 && z < do_get_expected_mipmap_num_pages(cdata, n), false);
+  nassertr(cdata->_ram_image_compression == CM_off, false);
+
+  return convert_to_pfm(pfm,
+                        do_get_expected_mipmap_x_size(cdata, n),
+                        do_get_expected_mipmap_y_size(cdata, n),
+                        cdata->_num_components, cdata->_component_width,
+                        cdata->_ram_images[n]._image,
+                        do_get_ram_mipmap_page_size(cdata, n), z);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::do_write_txo_file
 //       Access: Private
@@ -4497,28 +4644,32 @@ bool Texture::
 do_reconsider_image_properties(CData *cdata, int x_size, int y_size, int num_components,
                                Texture::ComponentType component_type, int z,
                                const LoaderOptions &options) {
-  if (!cdata->_loaded_from_image || num_components != cdata->_num_components) {
+  if (!cdata->_loaded_from_image || num_components != cdata->_num_components || component_type != cdata->_component_type) {
     // Come up with a default format based on the number of channels.
     // But only do this the first time the file is loaded, or if the
     // number of channels in the image changes on subsequent loads.
 
     switch (num_components) {
     case 1:
-      cdata->_format = F_luminance;
+      if (component_type == T_float) {
+        cdata->_format = F_depth_component;
+      } else {
+        cdata->_format = F_luminance;
+      }
       break;
-
+      
     case 2:
       cdata->_format = F_luminance_alpha;
       break;
-
+      
     case 3:
       cdata->_format = F_rgb;
       break;
-
+      
     case 4:
       cdata->_format = F_rgba;
       break;
-
+      
     default:
       // Eh?
       nassertr(false, false);
@@ -5711,6 +5862,35 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size, int z,
   nassertv(p == &image[idx] + page_size);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::convert_from_pfm
+//       Access: Private, Static
+//  Description: Internal method to convert pixel data from the
+//               indicated PfmFile into the given ram_image.
+////////////////////////////////////////////////////////////////////
+void Texture::
+convert_from_pfm(PTA_uchar &image, size_t page_size, int z,
+                 const PfmFile &pfm, int num_components, int component_width) {
+  nassertv(component_width == 4);  // Currently only PN_float32 is expected.
+  int x_size = pfm.get_x_size();
+  int y_size = pfm.get_y_size();
+
+  int idx = page_size * 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_component(i, j, c);
+        ++p;
+      }
+    }
+  }
+
+  nassertv((unsigned char *)p == &image[idx] + page_size);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::convert_to_pnmimage
 //       Access: Private, Static
@@ -5769,6 +5949,36 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::convert_to_pfm
+//       Access: Private, Static
+//  Description: Internal method to convert pixel data to the
+//               indicated PfmFile from the given ram_image.
+////////////////////////////////////////////////////////////////////
+bool Texture::
+convert_to_pfm(PfmFile &pfm, int x_size, int y_size,
+               int num_components, int component_width,
+               CPTA_uchar image, size_t page_size, int z) {
+  nassertr(component_width == 4, false);  // Currently only PN_float32 is expected.
+  pfm.clear(x_size, y_size, num_components);
+
+  int idx = page_size * z;
+  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_component(i, j, c, *p);
+        ++p;
+      }
+    }
+  }
+
+  nassertr((unsigned char *)p == &image[idx] + page_size, false);
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::read_dds_level_bgr8
 //       Access: Private, Static

+ 12 - 0
panda/src/gobj/texture.h

@@ -41,6 +41,7 @@
 #include "pipelineCycler.h"
 
 class PNMImage;
+class PfmFile;
 class TextureContext;
 class FactoryParams;
 class PreparedGraphicsObjects;
@@ -541,6 +542,9 @@ protected:
   virtual bool do_load_one(CData *cdata,
                            const PNMImage &pnmimage, const string &name,
                            int z, int n, const LoaderOptions &options);
+  virtual bool do_load_one(CData *cdata,
+                           const PfmFile &pfm, const string &name,
+                           int z, int n, const LoaderOptions &options);
   bool do_read_txo_file(CData *cdata, const Filename &fullpath);
   bool do_read_txo(CData *cdata, istream &in, const string &filename);
   bool do_read_dds_file(CData *cdata, const Filename &fullpath, bool header_only);
@@ -550,6 +554,7 @@ protected:
                 bool write_pages, bool write_mipmaps);
   bool do_write_one(CData *cdata, const Filename &fullpath, int z, int n);
   bool do_store_one(CData *cdata, PNMImage &pnmimage, int z, int n);
+  bool do_store_one(CData *cdata, PfmFile &pfm, int z, int n);
   bool do_write_txo_file(const CData *cdata, const Filename &fullpath) const;
   bool do_write_txo(const CData *cdata, ostream &out, const string &filename) const;
 
@@ -656,10 +661,17 @@ private:
   static void convert_from_pnmimage(PTA_uchar &image, size_t page_size, 
                                     int z, const PNMImage &pnmimage,
                                     int num_components, int component_width);
+  static void convert_from_pfm(PTA_uchar &image, size_t page_size, 
+                               int z, const PfmFile &pfm, 
+                               int num_components, int component_width);
   static bool convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
                                   int num_components, int component_width,
                                   CPTA_uchar image, size_t page_size, 
                                   int z);
+  static bool convert_to_pfm(PfmFile &pfm, int x_size, int y_size,
+                             int num_components, int component_width,
+                             CPTA_uchar image, size_t page_size, 
+                             int z);
   static PTA_uchar read_dds_level_bgr8(Texture *tex, CData *cdata, const DDSHeader &header, 
                                        int n, istream &in);
   static PTA_uchar read_dds_level_rgb8(Texture *tex, CData *cdata, const DDSHeader &header, 

+ 0 - 3
panda/src/pnmimage/Sources.pp

@@ -12,7 +12,6 @@
   #define SOURCES \
      config_pnmimage.h \
      pfmFile.I pfmFile.h \
-     pnmFileTypePfm.h \
      pnmbitio.h \
      pnmBrush.h pnmBrush.I \
      pnmFileType.h pnmFileTypeRegistry.h pnmImage.I  \
@@ -25,7 +24,6 @@
   #define INCLUDED_SOURCES \
      config_pnmimage.cxx \
      pfmFile.cxx \
-     pnmFileTypePfm.cxx \
      pnm-image-filter.cxx \
      pnmbitio.cxx \
      pnmBrush.cxx \
@@ -38,7 +36,6 @@
   #define INSTALL_HEADERS \
      config_pnmimage.h \
      pfmFile.I pfmFile.h \
-     pnmFileTypePfm.h \
      pnmBrush.h pnmBrush.I \
      pnmFileType.h pnmFileTypeRegistry.h pnmImage.I \
      pnmImage.h pnmImageHeader.I pnmImageHeader.h \

+ 0 - 6
panda/src/pnmimage/config_pnmimage.cxx

@@ -14,7 +14,6 @@
 
 #include "config_pnmimage.h"
 #include "pnmFileType.h"
-#include "pnmFileTypePfm.h"
 #include "pnmFileTypeRegistry.h"
 
 #include "dconfig.h"
@@ -66,9 +65,4 @@ init_libpnmimage() {
   initialized = true;
 
   PNMFileType::init_type();
-
-  PNMFileTypeRegistry *tr = PNMFileTypeRegistry::get_global_ptr();
-  PNMFileTypePfm::init_type();
-  PNMFileTypePfm::register_with_read_factory();
-  tr->register_type(new PNMFileTypePfm);
 }

+ 0 - 1
panda/src/pnmimage/p3pnmimage_composite1.cxx

@@ -1,6 +1,5 @@
 #include "config_pnmimage.cxx"
 #include "pfmFile.cxx"
-#include "pnmFileTypePfm.cxx"
 #include "pnm-image-filter.cxx"
 #include "pnmbitio.cxx"
 #include "pnmBrush.cxx"

+ 77 - 46
panda/src/pnmimage/pfmFile.I

@@ -23,26 +23,6 @@ is_valid() const {
   return _num_channels != 0 && (_x_size * _y_size * _num_channels <= (int)_table.size());
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: PfmFile::get_x_size
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE int PfmFile::
-get_x_size() const {
-  return _x_size;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PfmFile::get_y_size
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE int PfmFile::
-get_y_size() const {
-  return _y_size;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: PfmFile::get_scale
 //       Access: Published
@@ -55,18 +35,14 @@ get_scale() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PfmFile::get_num_channels
+//     Function: PfmFile::set_scale
 //       Access: Published
-//  Description: A pfm file can be either 1-channel
-//               (get_num_channels() == 1) or 3-channel
-//               (get_num_channels() == 3).  In the case of a
-//               1-channel file, the values can be found in the x
-//               component of each point, and the y and z components
-//               are unused.
+//  Description: The "scale" is reported in the pfm header and is
+//               probably meaningless.
 ////////////////////////////////////////////////////////////////////
-INLINE int PfmFile::
-get_num_channels() const {
-  return _num_channels;
+INLINE void PfmFile::
+set_scale(PN_float32 scale) {
+  _scale = scale;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -82,6 +58,34 @@ has_point(int x, int y) const {
   return _has_point(this, x, y);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PfmFile::get_component
+//       Access: Published
+//  Description: Returns the cth component of the point value at the
+//               indicated point.
+////////////////////////////////////////////////////////////////////
+INLINE PN_float32 PfmFile::
+get_component(int x, int y, int c) const {
+  nassertr(x >= 0 && x < _x_size &&
+           y >= 0 && y < _y_size &&
+           c >= 0 && c < _num_channels, 0.0f);
+  return _table[(y * _x_size + x) * _num_channels + c];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PfmFile::set_component
+//       Access: Published
+//  Description: Replaces the cth component of the point value at the
+//               indicated point.
+////////////////////////////////////////////////////////////////////
+INLINE void PfmFile::
+set_component(int x, int y, int c, PN_float32 value) {
+  nassertv(x >= 0 && x < _x_size &&
+           y >= 0 && y < _y_size &&
+           c >= 0 && c < _num_channels);
+  _table[(y * _x_size + x) * _num_channels + c] = value;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PfmFile::get_point1
 //       Access: Published
@@ -90,8 +94,8 @@ has_point(int x, int y) const {
 ////////////////////////////////////////////////////////////////////
 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);
+  nassertr(x >= 0 && x < _x_size &&
+           y >= 0 && y < _y_size, 0.0);
   return _table[(y * _x_size + x) * _num_channels];
 }
 
@@ -104,8 +108,8 @@ get_point1(int x, int y) const {
 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);
+  nassertv(x >= 0 && x < _x_size &&
+           y >= 0 && y < _y_size);
   _table[(y * _x_size + x) * _num_channels] = point;
 }
 
@@ -118,8 +122,8 @@ set_point1(int x, int y, PN_float32 point) {
 ////////////////////////////////////////////////////////////////////
 INLINE const LPoint3f &PfmFile::
 get_point(int x, int y) const {
-  nassertr(x >= 0 && x < _x_size, LPoint3f::zero());
-  nassertr(y >= 0 && y < _y_size, LPoint3f::zero());
+  nassertr(x >= 0 && x < _x_size &&
+           y >= 0 && y < _y_size, LPoint3f::zero());
   return *(LPoint3f *)&_table[(y * _x_size + x) * _num_channels];
 }
 
@@ -133,8 +137,8 @@ get_point(int x, int y) const {
 INLINE void PfmFile::
 set_point(int x, int y, const LVecBase3f &point) {
   nassertv(!point.is_nan());
-  nassertv(x >= 0 && x < _x_size);
-  nassertv(y >= 0 && y < _y_size);
+  nassertv(x >= 0 && x < _x_size &&
+           y >= 0 && y < _y_size);
   switch (_num_channels) {
   case 1:
     _table[(y * _x_size + x)] = point[0];
@@ -169,8 +173,8 @@ INLINE LPoint3f &PfmFile::
 modify_point(int x, int y) {
 #ifndef NDEBUG
   static LPoint3f dummy_value = LPoint3f::zero();
-  nassertr(x >= 0 && x < _x_size, dummy_value);
-  nassertr(y >= 0 && y < _y_size, dummy_value);
+  nassertr(x >= 0 && x < _x_size &&
+           y >= 0 && y < _y_size, dummy_value);
 #endif
 
   return *(LPoint3f *)&_table[(y * _x_size + x) * _num_channels];
@@ -185,8 +189,8 @@ modify_point(int x, int y) {
 ////////////////////////////////////////////////////////////////////
 INLINE const LPoint4f &PfmFile::
 get_point4(int x, int y) const {
-  nassertr(x >= 0 && x < _x_size, LPoint4f::zero());
-  nassertr(y >= 0 && y < _y_size, LPoint4f::zero());
+  nassertr(x >= 0 && x < _x_size &&
+           y >= 0 && y < _y_size, LPoint4f::zero());
   return *(LPoint4f *)&_table[(y * _x_size + x) * _num_channels];
 }
 
@@ -200,8 +204,8 @@ get_point4(int x, int y) const {
 INLINE void PfmFile::
 set_point4(int x, int y, const LVecBase4f &point) {
   nassertv(!point.is_nan());
-  nassertv(x >= 0 && x < _x_size);
-  nassertv(y >= 0 && y < _y_size);
+  nassertv(x >= 0 && x < _x_size &&
+           y >= 0 && y < _y_size);
   switch (_num_channels) {
   case 1:
     _table[(y * _x_size + x)] = point[0];
@@ -239,8 +243,8 @@ INLINE LPoint4f &PfmFile::
 modify_point4(int x, int y) {
 #ifndef NDEBUG
   static LPoint4f dummy_value = LPoint4f::zero();
-  nassertr(x >= 0 && x < _x_size, dummy_value);
-  nassertr(y >= 0 && y < _y_size, dummy_value);
+  nassertr(x >= 0 && x < _x_size &&
+           y >= 0 && y < _y_size, dummy_value);
 #endif
 
   return *(LPoint4f *)&_table[(y * _x_size + x) * _num_channels];
@@ -517,3 +521,30 @@ INLINE bool PfmFile::
 get_vis_2d() const {
   return _vis_2d;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: PfmFile::get_table
+//       Access: Public
+//  Description: This is a very low-level function that returns a
+//               read-only reference to the internal table of
+//               floating-point numbers.  Use this method at your own
+//               risk.
+////////////////////////////////////////////////////////////////////
+INLINE const pvector<PN_float32> &PfmFile::
+get_table() const {
+  return _table;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PfmFile::swap_table
+//       Access: Public
+//  Description: This is a very low-level function that completely
+//               exchanges the PfmFile's internal table of
+//               floating-point numbers with whatever you supply.  The
+//               provided table must have an appropriate size.  Use
+//               this method at your own risk.
+////////////////////////////////////////////////////////////////////
+void PfmFile::
+swap_table(pvector<PN_float32> &table) {
+  _table.swap(table);
+}

+ 85 - 126
panda/src/pnmimage/pfmFile.cxx

@@ -27,6 +27,7 @@
 #include "geomTriangles.h"
 #include "geomVertexWriter.h"
 #include "pnmImage.h"
+#include "pnmReader.h"
 #include "pnmWriter.h"
 #include "string_utils.h"
 #include "lens.h"
@@ -54,11 +55,9 @@ PfmFile() {
 ////////////////////////////////////////////////////////////////////
 PfmFile::
 PfmFile(const PfmFile &copy) :
+  PNMImageHeader(copy),
   _table(copy._table),
-  _x_size(copy._x_size),
-  _y_size(copy._y_size),
   _scale(copy._scale),
-  _num_channels(copy._num_channels),
   _has_no_data_value(copy._has_no_data_value),
   _no_data_value(copy._no_data_value),
   _has_point(copy._has_point),
@@ -74,11 +73,9 @@ PfmFile(const PfmFile &copy) :
 ////////////////////////////////////////////////////////////////////
 void PfmFile::
 operator = (const PfmFile &copy) {
+  PNMImageHeader::operator = (copy);
   _table = copy._table;
-  _x_size = copy._x_size;
-  _y_size = copy._y_size;
   _scale = copy._scale;
-  _num_channels = copy._num_channels;
   _has_no_data_value = copy._has_no_data_value;
   _no_data_value = copy._no_data_value;
   _has_point = copy._has_point;
@@ -108,7 +105,6 @@ clear() {
 ////////////////////////////////////////////////////////////////////
 void PfmFile::
 clear(int x_size, int y_size, int num_channels) {
-  nassertv(num_channels == 1 || num_channels == 3);
   nassertv(x_size >= 0 && y_size >= 0);
   _x_size = x_size;
   _y_size = y_size;
@@ -172,108 +168,49 @@ read(const Filename &fullpath) {
 //               to a floating-point type.
 ////////////////////////////////////////////////////////////////////
 bool PfmFile::
-read(istream &in, const Filename &fullpath, const string &magic_number) {
-  clear();
+read(istream &in, const Filename &fullpath) {
+  PNMReader *reader = make_reader(&in, false, fullpath);
+  if (reader == (PNMReader *)NULL) {
+    clear();
+    return false;
+  }
+  return read(reader);
+}
 
-  string identifier = magic_number;
-  PNMImageHeader::read_magic_number(&in, identifier, 2);
+////////////////////////////////////////////////////////////////////
+//     Function: PfmFile::read
+//       Access: Published
+//  Description: Reads the PFM data using the indicated PNMReader.
+//
+//               The PNMReader is always deleted upon completion,
+//               whether successful or not.
+////////////////////////////////////////////////////////////////////
+bool PfmFile::
+read(PNMReader *reader) {
+  clear();
 
-  if (identifier == "pf") {
-    // In this case, we're probably reading a special-extension
-    // 4-channel pfm file, and we need a four-byte magic number to
-    // confirm this and fully identify the file format.
-    PNMImageHeader::read_magic_number(&in, identifier, 4);
+  if (reader == NULL) {
+    return false;
   }
 
-  if (identifier == "PF") {
-    _num_channels = 3;
-
-  } else if (identifier == "Pf") {
-    _num_channels = 1;
+  if (!reader->is_valid()) {
+    delete reader;
+    return false;
+  }
 
-  } else if (identifier == "pf4c") {
-    _num_channels = 4;
-    
-  } else {
-    // Not a PFM file.  Maybe it's a more conventional image file that
-    // we can read into a PFM.
+  if (!reader->is_floating_point()) {
+    // Not a floating-point file.  Quietly convert it.
     PNMImage pnm;
-    PNMReader *reader = pnm.make_reader
-      (&in, false, fullpath, identifier, NULL, false);
-    if (reader == (PNMReader *)NULL) {
-      pnmimage_cat.error()
-        << "Not a PFM file or known image file type: " << fullpath << "\n";
-      return false;
-    }
-
     if (!pnm.read(reader)) {
-      pnmimage_cat.error()
-        << "Invalid image file: " << fullpath << "\n";
       return false;
     }
 
     return load(pnm);
   }
 
-  int width, height;
-  PN_float32 scale;
-  in >> width >> height >> scale;
-  if (!in) {
-    pnmimage_cat.error()
-      << "Error parsing PFM header: " << fullpath << "\n";
-    return false;
-  }
-
-  // Skip the last newline/whitespace character before the raw data
-  // begins.
-  in.get();
-
-  bool little_endian = false;
-  if (scale < 0) {
-    scale = -scale;
-    little_endian = true;
-  }
-  if (pfm_force_littleendian) {
-    little_endian = true;
-  }
-  if (pfm_reverse_dimensions) {
-    int t = width;
-    width = height;
-    height = t;
-  }
-
-  _x_size = width;
-  _y_size = height;
-  _scale = scale;
-
-  // So far, so good.  Now read the data.
-  int size = _x_size * _y_size * _num_channels;
-
-  // 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()) {
-    return false;
-  }
-
-  // Now we may have to endian-reverse the data.
-#ifdef WORDS_BIGENDIAN
-  bool endian_reversed = little_endian;
-#else
-  bool endian_reversed = !little_endian;
-#endif
-
-  if (endian_reversed) {
-    for (int ti = 0; ti < size; ++ti) {
-      ReversedNumericData nd(&_table[ti], sizeof(PN_float32));
-      nd.store_value(&_table[ti], sizeof(PN_float32));
-    }
-  }
-
-  return true;
+  bool success = reader->read_pfm(*this);
+  delete reader;
+  return success;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -335,45 +272,56 @@ write(const Filename &fullpath) {
 ////////////////////////////////////////////////////////////////////
 bool PfmFile::
 write(ostream &out, const Filename &fullpath) {
-  nassertr(is_valid(), false);
-
-  switch (_num_channels) {
-  case 1:
-    out << "Pf\n";
-    break;
+  if (!is_valid()) {
+    return false;
+  }
 
-  case 3:
-    out << "PF\n";
-    break;
+  PNMWriter *writer = make_writer(fullpath);
+  if (writer == (PNMWriter *)NULL) {
+    return false;
+  }
 
-  case 4:
-    out << "pf4c\n";
-    break;
+  return write(writer);
+}
 
-  default:
-    nassertr(false, false);
+////////////////////////////////////////////////////////////////////
+//     Function: PfmFile::write
+//       Access: Published
+//  Description: Writes the PFM data using the indicated PNMWriter.
+//
+//               The PNMWriter is always deleted upon completion,
+//               whether successful or not.
+////////////////////////////////////////////////////////////////////
+bool PfmFile::
+write(PNMWriter *writer) {
+  if (writer == NULL) {
+    return false;
   }
-  out << _x_size << " " << _y_size << "\n";
 
-  PN_float32 scale = cabs(_scale);
-  if (scale == 0.0f) {
-    scale = 1.0f;
+  if (!is_valid()) {
+    delete writer;
+    return false;
   }
-#ifndef WORDS_BIGENDIAN
-  // Little-endian computers must write a negative scale to indicate
-  // the little-endian nature of the output.
-  scale = -scale;
-#endif
-  out << scale << "\n";
 
-  int size = _x_size * _y_size * _num_channels;
-  out.write((const char *)&_table[0], sizeof(PN_float32) * size);
+  writer->copy_header_from(*this);
 
-  if (out.fail()) {
-    return false;
+  if (!writer->supports_floating_point()) {
+    // Hmm, it's an integer file type.  Convert it from the
+    // floating-point data we have.
+    PNMImage pnmimage;
+    if (!store(pnmimage)) {
+      delete writer;
+      return false;
+    }
+    writer->copy_header_from(pnmimage);
+    bool success = writer->write_data(pnmimage.get_array(), pnmimage.get_alpha_array());
+    delete writer;
+    return success;
   }
-  nassertr(sizeof(PN_float32) == 4, false);
-  return true;
+
+  bool success = writer->write_pfm(*this);
+  delete writer;
+  return success;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1436,6 +1384,17 @@ generate_vis_mesh(MeshFace face) const {
   return NodePath(gnode);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PfmFile::output
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+void PfmFile::
+output(ostream &out) const {
+  out << "floating-point image: " << _x_size << " by " << _y_size << " pixels, "
+      << _num_channels << " channels.";
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PfmFile::make_vis_mesh_geom
 //       Access: Private

+ 16 - 9
panda/src/pnmimage/pfmFile.h

@@ -16,6 +16,7 @@
 #define PFMFILE_H
 
 #include "pandabase.h"
+#include "pnmImageHeader.h"
 #include "luse.h"
 #include "nodePath.h"
 #include "boundingHexahedron.h"
@@ -25,6 +26,8 @@
 class GeomNode;
 class Lens;
 class PNMImage;
+class PNMReader;
+class PNMWriter;
 class GeomVertexWriter;
 
 ////////////////////////////////////////////////////////////////////
@@ -33,7 +36,7 @@ class GeomVertexWriter;
 //               numbers, either 3-component or 1-component, or with a
 //               special extension, 4-component.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_PNMIMAGE PfmFile {
+class EXPCL_PANDA_PNMIMAGE PfmFile : public PNMImageHeader {
 PUBLISHED:
   PfmFile();
   PfmFile(const PfmFile &copy);
@@ -43,22 +46,23 @@ PUBLISHED:
   void clear(int x_size, int y_size, int num_channels);
 
   BLOCKING bool read(const Filename &fullpath);
-  BLOCKING bool read(istream &in, const Filename &fullpath = Filename(),
-                     const string &magic_number = string());
+  BLOCKING bool read(istream &in, const Filename &fullpath = Filename());
+  BLOCKING bool read(PNMReader *reader);
   BLOCKING bool write(const Filename &fullpath);
   BLOCKING bool write(ostream &out, const Filename &fullpath = Filename());
+  BLOCKING bool write(PNMWriter *writer);
 
   BLOCKING bool load(const PNMImage &pnmimage);
   BLOCKING bool store(PNMImage &pnmimage) const;
 
   INLINE bool is_valid() const;
 
-  INLINE int get_x_size() const;
-  INLINE int get_y_size() const;
   INLINE PN_float32 get_scale() const;
-  INLINE int get_num_channels() const;
+  INLINE void set_scale(PN_float32 scale);
 
   INLINE bool has_point(int x, int y) const;
+  INLINE PN_float32 get_component(int x, int y, int c) const;
+  INLINE void set_component(int x, int y, int c, PN_float32 value);
   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;
@@ -131,6 +135,12 @@ PUBLISHED:
   };
   BLOCKING NodePath generate_vis_mesh(MeshFace face = MF_front) const;
 
+  void output(ostream &out) const;
+
+public:
+  INLINE const pvector<PN_float32> &get_table() const;
+  INLINE void swap_table(pvector<PN_float32> &table);
+
 private:
   void make_vis_mesh_geom(GeomNode *gnode, bool inverted) const;
 
@@ -196,10 +206,7 @@ private:
   typedef pvector<PN_float32> Table;
   Table _table;
 
-  int _x_size;
-  int _y_size;
   PN_float32 _scale;
-  int _num_channels;
 
   bool _has_no_data_value;
   LPoint4f _no_data_value;

+ 26 - 0
panda/src/pnmimage/pnmImage.cxx

@@ -16,6 +16,7 @@
 #include "pnmReader.h"
 #include "pnmWriter.h"
 #include "pnmBrush.h"
+#include "pfmFile.h"
 #include "config_pnmimage.h"
 #include "perlinNoise2.h"
 #include "stackedPerlinNoise2.h"
@@ -307,6 +308,17 @@ read(PNMReader *reader) {
 
   copy_header_from(*reader);
 
+  if (reader->is_floating_point()) {
+    // Hmm, it's a floating-point file.  Quietly convert it to integer.
+    PfmFile pfm;
+    if (!reader->read_pfm(pfm)) {
+      delete reader;
+      return false;
+    }
+    delete reader;
+    return pfm.store(*this);
+  }
+
   // We reassign y_size after reading because we might have read a
   // truncated file.
   _y_size = reader->read_data(_array, _alpha);
@@ -400,6 +412,20 @@ write(PNMWriter *writer) const {
   }
 
   writer->copy_header_from(*this);
+
+  if (!writer->supports_integer()) {
+    // Hmm, it's only a floating-point file type.  Convert it from the
+    // integer data we have.
+    PfmFile pfm;
+    if (!pfm.load(*this)) {
+      delete writer;
+      return false;
+    }
+    bool success = writer->write_pfm(pfm);
+    delete writer;
+    return success;
+  }
+
   if (is_grayscale() && !writer->supports_grayscale()) {
     // Copy the gray values to all channels to help out the writer.
     for (int y = 0; y < get_y_size(); y++) {

+ 24 - 0
panda/src/pnmimage/pnmReader.cxx

@@ -70,6 +70,30 @@ prepare_read() {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PNMReader::is_floating_point
+//       Access: Public, Virtual
+//  Description: Returns true if this PNMFileType represents a
+//               floating-point image type, false if it is a normal,
+//               integer type.  If this returns true, read_pfm() is
+//               implemented instead of read_data().
+////////////////////////////////////////////////////////////////////
+bool PNMReader::
+is_floating_point() {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMReader::read_pfm
+//       Access: Public, Virtual
+//  Description: Reads floating-point data directly into the indicated
+//               PfmFile.  Returns true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+bool PNMReader::
+read_pfm(PfmFile &pfm) {
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMReader::read_data
 //       Access: Public, Virtual

+ 3 - 1
panda/src/pnmimage/pnmReader.h

@@ -18,7 +18,7 @@
 #include "pandabase.h"
 
 #include "pnmImageHeader.h"
-
+class PfmFile;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : PNMReader
@@ -39,6 +39,8 @@ public:
   INLINE PNMFileType *get_type() const;
 
   virtual void prepare_read();
+  virtual bool is_floating_point();
+  virtual bool read_pfm(PfmFile &pfm);
   virtual int read_data(xel *array, xelval *alpha);
   virtual bool supports_read_row() const;
   virtual bool read_row(xel *array, xelval *alpha, int x_size, int y_size);

+ 37 - 0
panda/src/pnmimage/pnmWriter.cxx

@@ -28,6 +28,43 @@ PNMWriter::
   _file = (ostream *)NULL;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PNMWriter::supports_floating_point
+//       Access: Public, Virtual
+//  Description: Returns true if this PNMFileType can accept a
+//               floating-point image type, false if it can only
+//               accept a normal, integer type.  If this returns true,
+//               write_pfm() is implemented.
+////////////////////////////////////////////////////////////////////
+bool PNMWriter::
+supports_floating_point() {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMWriter::supports_integer
+//       Access: Public, Virtual
+//  Description: Returns true if this PNMFileType can accept an
+//               integer image type, false if it can only
+//               accept a floating-point type.  If this returns true,
+//               write_data() or write_row() is implemented.
+////////////////////////////////////////////////////////////////////
+bool PNMWriter::
+supports_integer() {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMWriter::write_pfm
+//       Access: Public, Virtual
+//  Description: Writes floating-point data from the indicated
+//               PfmFile.  Returns true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+bool PNMWriter::
+write_pfm(const PfmFile &pfm) {
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMWriter::write_data
 //       Access: Public, Virtual

+ 5 - 0
panda/src/pnmimage/pnmWriter.h

@@ -18,6 +18,7 @@
 #include "pandabase.h"
 
 #include "pnmImageHeader.h"
+class PfmFile;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : PNMWriter
@@ -48,6 +49,10 @@ public:
 
   INLINE void copy_header_from(const PNMImageHeader &header);
 
+  virtual bool supports_floating_point();
+  virtual bool supports_integer();
+  virtual bool write_pfm(const PfmFile &pfm);
+
   virtual int write_data(xel *array, xelval *alpha);
   virtual bool supports_write_row() const;
   virtual bool supports_grayscale() const;

+ 2 - 0
panda/src/pnmimagetypes/Sources.pp

@@ -15,6 +15,7 @@
      pnmFileTypeIMG.h  \
      pnmFileTypePNG.h \
      pnmFileTypePNM.h \
+     pnmFileTypePfm.h \
      pnmFileTypeSGI.h pnmFileTypeSoftImage.h  \
      pnmFileTypeTGA.h \
      pnmFileTypeTIFF.h \
@@ -28,6 +29,7 @@
      pnmFileTypeJPG.cxx pnmFileTypeJPGReader.cxx pnmFileTypeJPGWriter.cxx \
      pnmFileTypePNG.cxx \
      pnmFileTypePNM.cxx \
+     pnmFileTypePfm.cxx \
      pnmFileTypeSGI.cxx \
      pnmFileTypeSGIReader.cxx pnmFileTypeSGIWriter.cxx  \
      pnmFileTypeSoftImage.cxx \

+ 5 - 0
panda/src/pnmimagetypes/config_pnmimagetypes.cxx

@@ -21,6 +21,7 @@
 #include "pnmFileTypeJPG.h"
 #include "pnmFileTypePNG.h"
 #include "pnmFileTypePNM.h"
+#include "pnmFileTypePfm.h"
 #include "pnmFileTypeTIFF.h"
 #include "sgi.h"
 
@@ -216,6 +217,10 @@ init_libpnmimagetypes() {
   tr->register_type(new PNMFileTypePNM);
 #endif
 
+  PNMFileTypePfm::init_type();
+  PNMFileTypePfm::register_with_read_factory();
+  tr->register_type(new PNMFileTypePfm);
+
 #ifdef HAVE_JPEG
   PNMFileTypeJPG::init_type();
   PNMFileTypeJPG::register_with_read_factory();

+ 1 - 0
panda/src/pnmimagetypes/p3pnmimagetypes_composite2.cxx

@@ -1,6 +1,7 @@
 #include "pnmFileTypePNG.cxx"
 #include "pnmFileTypeTIFF.cxx"
 #include "pnmFileTypePNM.cxx"
+#include "pnmFileTypePfm.cxx"
 #include "pnmFileTypeSGI.cxx"
 #include "pnmFileTypeSGIReader.cxx"
 #include "pnmFileTypeSGIWriter.cxx"

+ 160 - 62
panda/src/pnmimage/pnmFileTypePfm.cxx → panda/src/pnmimagetypes/pnmFileTypePfm.cxx

@@ -13,6 +13,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "pnmFileTypePfm.h"
+#include "pfmFile.h"
 #include "config_pnmimage.h"
 
 #include "pnmFileTypeRegistry.h"
@@ -134,45 +135,115 @@ PNMFileTypePfm::Reader::
 Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
   PNMReader(type, file, owns_file)
 {
-  PfmFile pfm;
-  if (!pfm.read(*file, Filename(), magic_number)) {
+  read_magic_number(_file, magic_number, 2);
+
+  if (magic_number == "pf") {
+    // In this case, we're probably reading a special-extension
+    // 4-channel pfm file, and we need a four-byte magic number to
+    // confirm this and fully identify the file format.
+    read_magic_number(_file, magic_number, 4);
+  }
+
+  if (magic_number == "PF") {
+    _num_channels = 3;
+
+  } else if (magic_number == "Pf") {
+    _num_channels = 1;
+
+  } else if (magic_number == "pf4c") {
+    // Special DRZ extension.
+    _num_channels = 4;
+    
+  } else {
+    pnmimage_cat.debug()
+      << "Not a PFM file\n";
+    _is_valid = false;
+    return;
+  }
+
+  (*_file) >> _x_size >> _y_size >> _scale;
+  if (!(*_file)) {
+    pnmimage_cat.debug()
+      << "Error parsing PFM header\n";
     _is_valid = false;
     return;
   }
 
-  pfm.store(_image);
-  PNMImageHeader::operator = (_image);
+  // Skip the last newline/whitespace character before the raw data
+  // begins.
+  (*_file).get();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PNMFileTypePfm::Reader::read_data
+//     Function: PNMFileTypePfm::Reader::is_floating_point
 //       Access: Public, Virtual
-//  Description: Reads in an entire image all at once, storing it in
-//               the pre-allocated _x_size * _y_size array and alpha
-//               pointers.  (If the image type has no alpha channel,
-//               alpha is ignored.)  Returns the number of rows
-//               correctly read.
-//
-//               Derived classes need not override this if they
-//               instead provide supports_read_row() and read_row(),
-//               below.
+//  Description: Returns true if this PNMFileType represents a
+//               floating-point image type, false if it is a normal,
+//               integer type.  If this returns true, read_pfm() is
+//               implemented instead of read_data().
 ////////////////////////////////////////////////////////////////////
-int PNMFileTypePfm::Reader::
-read_data(xel *array, xelval *alpha) {
+bool PNMFileTypePfm::Reader::
+is_floating_point() {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::Reader::read_pfm
+//       Access: Public, Virtual
+//  Description: Reads floating-point data directly into the indicated
+//               PfmFile.  Returns true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+bool PNMFileTypePfm::Reader::
+read_pfm(PfmFile &pfm) {
   if (!is_valid()) {
-    return 0;
+    return false;
+  }
+
+  bool little_endian = false;
+  if (_scale < 0) {
+    _scale = -_scale;
+    little_endian = true;
   }
+  if (pfm_force_littleendian) {
+    little_endian = true;
+  }
+  if (pfm_reverse_dimensions) {
+    int t = _x_size;
+    _x_size = _y_size;
+    _y_size = t;
+  }
+
+  pfm.clear(_x_size, _y_size, _num_channels);
+  pfm.set_scale(_scale);
+
+  // So far, so good.  Now read the data.
+  int size = _x_size * _y_size * _num_channels;
+
+  pvector<PN_float32> table;
+  pfm.swap_table(table);
 
-  nassertr(_image.get_x_size() == get_x_size() &&
-           _image.get_y_size() == get_y_size(), 0);
+  (*_file).read((char *)&table[0], sizeof(PN_float32) * size);
+  if ((*_file).fail() && !(*_file).eof()) {
+    pfm.clear();
+    return false;
+  }
 
-  memcpy(array, _image.get_array(), get_x_size() * get_y_size() * sizeof(xel));
+  // Now we may have to endian-reverse the data.
+#ifdef WORDS_BIGENDIAN
+  bool endian_reversed = little_endian;
+#else
+  bool endian_reversed = !little_endian;
+#endif
 
-  if (has_alpha()) {
-    memcpy(alpha, _image.get_alpha_array(), get_x_size() * get_y_size() * sizeof(xelval));
+  if (endian_reversed) {
+    for (int ti = 0; ti < size; ++ti) {
+      ReversedNumericData nd(&table[ti], sizeof(PN_float32));
+      nd.store_value(&table[ti], sizeof(PN_float32));
+    }
   }
 
-  return get_y_size();
+  pfm.swap_table(table);
+  return true;
 }
 
 
@@ -188,52 +259,79 @@ Writer(PNMFileType *type, ostream *file, bool owns_file) :
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PNMFileTypePfm::write_data
+//     Function: PNMFileTypePfm::Writer::supports_floating_point
 //       Access: Public, Virtual
-//  Description: Writes out an entire image all at once, including the
-//               header, based on the image data stored in the given
-//               _x_size * _y_size array and alpha pointers.  (If the
-//               image type has no alpha channel, alpha is ignored.)
-//               Returns the number of rows correctly written.
-//
-//               It is the user's responsibility to fill in the header
-//               data via calls to set_x_size(), set_num_channels(),
-//               etc., or copy_header_from(), before calling
-//               write_data().
-//
-//               It is important to delete the PNMWriter class after
-//               successfully writing the data.  Failing to do this
-//               may result in some data not getting flushed!
-//
-//               Derived classes need not override this if they
-//               instead provide supports_streaming() and write_row(),
-//               below.
-////////////////////////////////////////////////////////////////////
-int PNMFileTypePfm::Writer::
-write_data(xel *array, xelval *alpha) {
-  if (_x_size <= 0 || _y_size <= 0) {
-    return 0;
-  }
+//  Description: Returns true if this PNMFileType can accept a
+//               floating-point image type, false if it can only
+//               accept a normal, integer type.  If this returns true,
+//               write_pfm() is implemented.
+////////////////////////////////////////////////////////////////////
+bool PNMFileTypePfm::Writer::
+supports_floating_point() {
+  return true;
+}
 
-  PNMImage image;
-  image.copy_header_from(*this);
-  nassertr(image.get_x_size() == get_x_size() && 
-           image.get_y_size() == get_y_size(), 0);
-  memcpy(image.get_array(), array, get_x_size() * get_y_size() * sizeof(xel));
-  if (has_alpha()) {
-    memcpy(image.get_alpha_array(), alpha, get_x_size() * get_y_size() * sizeof(xelval));
-  }
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::Writer::supports_integer
+//       Access: Public, Virtual
+//  Description: Returns true if this PNMFileType can accept an
+//               integer image type, false if it can only
+//               accept a floating-point type.  If this returns true,
+//               write_data() or write_row() is implemented.
+////////////////////////////////////////////////////////////////////
+bool PNMFileTypePfm::Writer::
+supports_integer() {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::Writer::write_pfm
+//       Access: Public, Virtual
+//  Description: Writes floating-point data from the indicated
+//               PfmFile.  Returns true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+bool PNMFileTypePfm::Writer::
+write_pfm(const PfmFile &pfm) {
+  nassertr(pfm.is_valid(), false);
+
+  switch (pfm.get_num_channels()) {
+  case 1:
+    (*_file) << "Pf\n";
+    break;
 
-  PfmFile pfm;
-  if (!pfm.load(image)) {
-    return 0;
+  case 3:
+    (*_file) << "PF\n";
+    break;
+
+  case 4:
+    (*_file) << "pf4c\n";
+    break;
+
+  default:
+    nassertr(false, false);
   }
+  (*_file) << pfm.get_x_size() << " " << pfm.get_y_size() << "\n";
 
-  if (!pfm.write(*_file)) {
-    return 0;
+  PN_float32 scale = cabs(pfm.get_scale());
+  if (scale == 0.0f) {
+    scale = 1.0f;
   }
+#ifndef WORDS_BIGENDIAN
+  // Little-endian computers must write a negative scale to indicate
+  // the little-endian nature of the output.
+  scale = -scale;
+#endif
+  (*_file) << scale << "\n";
+
+  int size = pfm.get_x_size() * pfm.get_y_size() * pfm.get_num_channels();
+  const pvector<PN_float32> &table = pfm.get_table();
+  (*_file).write((const char *)&table[0], sizeof(PN_float32) * size);
 
-  return get_y_size();
+  if ((*_file).fail()) {
+    return false;
+  }
+  nassertr(sizeof(PN_float32) == 4, false);
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 7 - 4
panda/src/pnmimage/pnmFileTypePfm.h → panda/src/pnmimagetypes/pnmFileTypePfm.h

@@ -48,18 +48,21 @@ public:
   class Reader : public PNMReader {
   public:
     Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number);
-
-    virtual int read_data(xel *array, xelval *alpha);
+    
+    virtual bool is_floating_point();
+    virtual bool read_pfm(PfmFile &pfm);
 
   private:
-    PNMImage _image;
+    PN_float32 _scale;
   };
 
   class Writer : public PNMWriter {
   public:
     Writer(PNMFileType *type, ostream *file, bool owns_file);
 
-    virtual int write_data(xel *array, xelval *alpha);
+    virtual bool supports_floating_point();
+    virtual bool supports_integer();
+    virtual bool write_pfm(const PfmFile &pfm);
   };
 
 

+ 2 - 0
panda/src/putil/loaderOptions.h

@@ -46,6 +46,8 @@ PUBLISHED:
     TF_allow_1d          = 0x0010,  // If texture is Nx1, make a 1-d texture
     TF_generate_mipmaps  = 0x0020,  // Consider generating mipmaps
     TF_multiview         = 0x0040,  // Load a multiview texture in pages
+    TF_integer           = 0x0080,  // Load as an integer (RGB) texture
+    TF_float             = 0x0100,  // Load as a floating-point (depth) texture
   };
 
   LoaderOptions(int flags = LF_search | LF_report_errors);