Bläddra i källkod

integrate pfm files more tightly with other image file types

David Rose 13 år sedan
förälder
incheckning
c64a59d62e

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

@@ -26,6 +26,7 @@
     multitexReducer.I multitexReducer.h multitexReducer.cxx \
     nodeVertexTransform.I nodeVertexTransform.h \
     pfmFile.I pfmFile.h \
+    pnmFileTypePfm.h \
     rigidBodyCombiner.I rigidBodyCombiner.h
     
   #define INCLUDED_SOURCES \
@@ -41,6 +42,7 @@
     heightfieldTesselator.cxx \
     nodeVertexTransform.cxx \    
     pfmFile.cxx \
+    pnmFileTypePfm.cxx \
     pipeOcclusionCullTraverser.cxx \
     lineSegs.cxx \
     rigidBodyCombiner.cxx
@@ -59,6 +61,7 @@
     multitexReducer.I multitexReducer.h \
     nodeVertexTransform.I nodeVertexTransform.h \
     pfmFile.I pfmFile.h \
+    pnmFileTypePfm.h \
     rigidBodyCombiner.I rigidBodyCombiner.h
 
   #define IGATESCAN all

+ 7 - 0
panda/src/grutil/config_grutil.cxx

@@ -24,6 +24,8 @@
 #include "nodeVertexTransform.h"
 #include "rigidBodyCombiner.h"
 #include "pipeOcclusionCullTraverser.h"
+#include "pnmFileTypePfm.h"
+#include "pnmFileTypeRegistry.h"
 
 #include "dconfig.h"
 
@@ -132,5 +134,10 @@ init_libgrutil() {
   TexturePool *ts = TexturePool::get_global_ptr();
   ts->register_texture_type(MovieTexture::make_texture, "avi mov mpg mpeg mp4 wmv asf flv nut ogm mkv");
 #endif  // HAVE_FFMPEG
+
+  PNMFileTypeRegistry *tr = PNMFileTypeRegistry::get_global_ptr();
+  PNMFileTypePfm::init_type();
+  PNMFileTypePfm::register_with_read_factory();
+  tr->register_type(new PNMFileTypePfm);
 }
 

+ 1 - 0
panda/src/grutil/p3grutil_composite2.cxx

@@ -5,3 +5,4 @@
 #include "meshDrawer.cxx"
 #include "meshDrawer2D.cxx"
 #include "pfmFile.cxx"
+#include "pnmFileTypePfm.cxx"

+ 127 - 10
panda/src/grutil/pfmFile.cxx

@@ -26,6 +26,9 @@
 #include "geomPoints.h"
 #include "geomTriangles.h"
 #include "geomVertexWriter.h"
+#include "pnmImage.h"
+#include "pnmWriter.h"
+#include "string_utils.h"
 #include "lens.h"
 #include "look_at.h"
 
@@ -118,6 +121,10 @@ clear(int x_size, int y_size, int num_channels) {
 //       Access: Published
 //  Description: Reads the PFM data from the indicated file, returning
 //               true on success, false on failure.
+//
+//               This can also handle reading a standard image file
+//               supported by PNMImage; it will be quietly converted
+//               to a floating-point type.
 ////////////////////////////////////////////////////////////////////
 bool PfmFile::
 read(const Filename &fullpath) {
@@ -138,7 +145,7 @@ read(const Filename &fullpath) {
   }
 
   istream *in = file->open_read_file(true);
-  bool success = read(*in);
+  bool success = read(*in, fullpath);
   vfs->close_read_file(in);
 
   return success;
@@ -149,22 +156,41 @@ read(const Filename &fullpath) {
 //       Access: Published
 //  Description: Reads the PFM data from the indicated stream,
 //               returning true on success, false on failure.
+//
+//               This can also handle reading a standard image file
+//               supported by PNMImage; it will be quietly converted
+//               to a floating-point type.
 ////////////////////////////////////////////////////////////////////
 bool PfmFile::
-read(istream &in) {
+read(istream &in, const Filename &fullpath, const string &magic_number) {
   clear();
 
-  string identifier;
-  in >> identifier;
+  string identifier = magic_number;
+  PNMImageHeader::read_magic_number(&in, identifier, 2);
 
   if (identifier == "PF") {
     _num_channels = 3;
   } else if (identifier == "Pf") {
     _num_channels = 1;
   } else {
-    grutil_cat.error()
-      << "Not a pfm file.\n";
-    return false;
+    // Not a PFM file.  Maybe it's a more conventional image file that
+    // we can read into a PFM.
+    PNMImage pnm;
+    PNMReader *reader = pnm.make_reader
+      (&in, false, fullpath, identifier, NULL, false);
+    if (reader == (PNMReader *)NULL) {
+      grutil_cat.error()
+        << "Not a PFM file or known image file type: " << fullpath << "\n";
+      return false;
+    }
+
+    if (!pnm.read(reader)) {
+      grutil_cat.error()
+        << "Invalid image file: " << fullpath << "\n";
+      return false;
+    }
+
+    return load(pnm);
   }
 
   int width, height;
@@ -172,7 +198,7 @@ read(istream &in) {
   in >> width >> height >> scale;
   if (!in) {
     grutil_cat.error()
-      << "Error parsing pfm header.\n";
+      << "Error parsing PFM header: " << fullpath << "\n";
     return false;
   }
 
@@ -247,9 +273,21 @@ read(istream &in) {
 //       Access: Published
 //  Description: Writes the PFM data to the indicated file, returning
 //               true on success, false on failure.
+//
+//               This can also handle writing a standard image file
+//               supported by PNMImage, if the filename extension is
+//               some image type's extension fother than "pfm"; it
+//               will be quietly converted to the appropriate integer
+//               type.
 ////////////////////////////////////////////////////////////////////
 bool PfmFile::
 write(const Filename &fullpath) {
+  if (!is_valid()) {
+    grutil_cat.error()
+      << "PFM file is invalid.\n";
+    return false;
+  }
+
   Filename filename = Filename::binary_filename(fullpath);
   pofstream out;
   if (!filename.open_write(out)) {
@@ -258,12 +296,27 @@ write(const Filename &fullpath) {
     return false;
   }
 
+  string extension = downcase(fullpath.get_extension());
+  if (extension != "pfm") {
+    // Maybe we're trying to write a different kind of image file.
+    PNMImage pnm;
+    PNMWriter *writer = pnm.make_writer(&out, false, fullpath, NULL);
+    if (writer != (PNMWriter *)NULL) {
+      // Yep.
+      if (store(pnm)) {
+        return pnm.write(writer);
+      }
+      // Couldn't make an image.  Carry on directly.
+      delete writer;
+    }
+  }
+  
   if (grutil_cat.is_debug()) {
     grutil_cat.debug()
       << "Writing PFM file " << filename << "\n";
   }
 
-  return write(out);
+  return write(out, fullpath);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -273,7 +326,7 @@ write(const Filename &fullpath) {
 //               returning true on success, false on failure.
 ////////////////////////////////////////////////////////////////////
 bool PfmFile::
-write(ostream &out) {
+write(ostream &out, const Filename &fullpath) {
   nassertr(is_valid(), false);
 
   if (_num_channels == 1) {
@@ -310,6 +363,70 @@ write(ostream &out) {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PfmFile::load
+//       Access: Published
+//  Description: Fills the PfmFile with the data from the indicated
+//               PNMImage, converted to floating-point values.
+////////////////////////////////////////////////////////////////////
+bool PfmFile::
+load(const PNMImage &pnmimage) {
+  if (!pnmimage.is_valid()) {
+    clear();
+    return false;
+  }
+
+  // Get the number of channels, ignoring alpha.
+  int num_channels;
+  if (pnmimage.get_num_channels() >= 3) {
+    num_channels = 3;
+  } else {
+    num_channels = 1;
+  }
+
+  clear(pnmimage.get_x_size(), pnmimage.get_y_size(), num_channels);
+  if (num_channels == 1) {
+    for (int yi = 0; yi < pnmimage.get_y_size(); ++yi) {
+      for (int xi = 0; xi < pnmimage.get_x_size(); ++xi) {
+        double gray = pnmimage.get_gray(xi, yi);
+        set_point(xi, yi, LVecBase3(gray, gray, gray));
+      }
+    }
+  } else {
+    for (int yi = 0; yi < pnmimage.get_y_size(); ++yi) {
+      for (int xi = 0; xi < pnmimage.get_x_size(); ++xi) {
+        LRGBColord xel = pnmimage.get_xel(xi, yi);
+        set_point(xi, yi, LVecBase3(xel[0], xel[1], xel[2]));
+      }
+    }
+  }
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PfmFile::store
+//       Access: Published
+//  Description: Copies the data to the indicated PNMImage, converting
+//               to RGB values.
+////////////////////////////////////////////////////////////////////
+bool PfmFile::
+store(PNMImage &pnmimage) const {
+  if (!is_valid()) {
+    pnmimage.clear();
+    return false;
+  }
+
+  pnmimage.clear(get_x_size(), get_y_size(), get_num_channels(), PGM_MAXMAXVAL);
+  for (int yi = 0; yi < get_y_size(); ++yi) {
+    for (int xi = 0; xi < get_x_size(); ++xi) {
+      LPoint3 point = get_point(xi, yi);
+      pnmimage.set_xel(xi, yi, point[0], point[1], point[2]);
+    }
+  }
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PfmFile::calc_average_point
 //       Access: Published

+ 6 - 2
panda/src/grutil/pfmFile.h

@@ -39,9 +39,13 @@ PUBLISHED:
   void clear(int x_size, int y_size, int num_channels);
 
   BLOCKING bool read(const Filename &fullpath);
-  BLOCKING bool read(istream &in);
+  BLOCKING bool read(istream &in, const Filename &fullpath = Filename(),
+                     const string &magic_number = string());
   BLOCKING bool write(const Filename &fullpath);
-  BLOCKING bool write(ostream &out);
+  BLOCKING bool write(ostream &out, const Filename &fullpath = Filename());
+
+  BLOCKING bool load(const PNMImage &pnmimage);
+  BLOCKING bool store(PNMImage &pnmimage) const;
 
   INLINE bool is_valid() const;
 

+ 258 - 0
panda/src/grutil/pnmFileTypePfm.cxx

@@ -0,0 +1,258 @@
+// Filename: pnmFileTypePfm.cxx
+// Created by:  drose (04Apr98)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pnmFileTypePfm.h"
+#include "config_grutil.h"
+
+#include "pnmFileTypeRegistry.h"
+#include "bamReader.h"
+
+TypeHandle PNMFileTypePfm::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+PNMFileTypePfm::
+PNMFileTypePfm() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::get_name
+//       Access: Public, Virtual
+//  Description: Returns a few words describing the file type.
+////////////////////////////////////////////////////////////////////
+string PNMFileTypePfm::
+get_name() const {
+  return "Portable Float Map";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::get_num_extensions
+//       Access: Public, Virtual
+//  Description: Returns the number of different possible filename
+//               extensions associated with this particular file type.
+////////////////////////////////////////////////////////////////////
+int PNMFileTypePfm::
+get_num_extensions() const {
+  return 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::get_extension
+//       Access: Public, Virtual
+//  Description: Returns the nth possible filename extension
+//               associated with this particular file type, without a
+//               leading dot.
+////////////////////////////////////////////////////////////////////
+string PNMFileTypePfm::
+get_extension(int n) const {
+  return "pfm";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::get_suggested_extension
+//       Access: Public, Virtual
+//  Description: Returns a suitable filename extension (without a
+//               leading dot) to suggest for files of this type, or
+//               empty string if no suggestions are available.
+////////////////////////////////////////////////////////////////////
+string PNMFileTypePfm::
+get_suggested_extension() const {
+  return "pfm";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::has_magic_number
+//       Access: Public, Virtual
+//  Description: Returns true if this particular file type uses a
+//               magic number to identify it, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool PNMFileTypePfm::
+has_magic_number() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::matches_magic_number
+//       Access: Public, Virtual
+//  Description: Returns true if the indicated "magic number" byte
+//               stream (the initial few bytes read from the file)
+//               matches this particular file type, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool PNMFileTypePfm::
+matches_magic_number(const string &magic_number) const {
+  return (magic_number.size() >= 2) &&
+    magic_number[0] == 'P' &&
+    (magic_number[1] == 'F' || magic_number[1] == 'f');
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::make_reader
+//       Access: Public, Virtual
+//  Description: Allocates and returns a new PNMReader suitable for
+//               reading from this file type, if possible.  If reading
+//               from this file type is not supported, returns NULL.
+////////////////////////////////////////////////////////////////////
+PNMReader *PNMFileTypePfm::
+make_reader(istream *file, bool owns_file, const string &magic_number) {
+  return new Reader(this, file, owns_file, magic_number);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::make_writer
+//       Access: Public, Virtual
+//  Description: Allocates and returns a new PNMWriter suitable for
+//               reading from this file type, if possible.  If writing
+//               files of this type is not supported, returns NULL.
+////////////////////////////////////////////////////////////////////
+PNMWriter *PNMFileTypePfm::
+make_writer(ostream *file, bool owns_file) {
+  return new Writer(this, file, owns_file);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::Reader::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+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)) {
+    _is_valid = false;
+    return;
+  }
+
+  pfm.store(_image);
+  PNMImageHeader::operator = (_image);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::Reader::read_data
+//       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.
+////////////////////////////////////////////////////////////////////
+int PNMFileTypePfm::Reader::
+read_data(xel *array, xelval *alpha) {
+  if (!is_valid()) {
+    return 0;
+  }
+
+  nassertr(_image.get_x_size() == get_x_size() &&
+           _image.get_y_size() == get_y_size(), 0);
+  memcpy(array, _image[0], get_x_size() * get_y_size() * sizeof(xel));
+  nassertr(!has_alpha(), 0);
+  return get_y_size();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::Writer::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+PNMFileTypePfm::Writer::
+Writer(PNMFileType *type, ostream *file, bool owns_file) :
+  PNMWriter(type, file, owns_file)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::write_data
+//       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;
+  }
+
+  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[0], array, get_x_size() * get_y_size() * sizeof(xel));
+  nassertr(!has_alpha(), 0);
+
+  PfmFile pfm;
+  if (!pfm.load(image)) {
+    return 0;
+  }
+
+  if (!pfm.write(*_file)) {
+    return 0;
+  }
+
+  return get_y_size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::register_with_read_factory
+//       Access: Public, Static
+//  Description: Registers the current object as something that can be
+//               read from a Bam file.
+////////////////////////////////////////////////////////////////////
+void PNMFileTypePfm::
+register_with_read_factory() {
+  BamReader::get_factory()->
+    register_factory(get_class_type(), make_PNMFileTypePfm);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypePfm::make_PNMFileTypePfm
+//       Access: Protected, Static
+//  Description: This method is called by the BamReader when an object
+//               of this type is encountered in a Bam file; it should
+//               allocate and return a new object with all the data
+//               read.
+//
+//               In the case of the PNMFileType objects, since these
+//               objects are all shared, we just pull the object from
+//               the registry.
+////////////////////////////////////////////////////////////////////
+TypedWritable *PNMFileTypePfm::
+make_PNMFileTypePfm(const FactoryParams &params) {
+  return PNMFileTypeRegistry::get_global_ptr()->get_type_by_handle(get_class_type());
+}

+ 92 - 0
panda/src/grutil/pnmFileTypePfm.h

@@ -0,0 +1,92 @@
+// Filename: pnmFileTypePfm.h
+// Created by:  drose (17Jun00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef PNMFILETYPEPFM_H
+#define PNMFILETYPEPFM_H
+
+#include "pandabase.h"
+
+#include "pnmFileType.h"
+#include "pnmReader.h"
+#include "pnmWriter.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : PNMFileTypePfm
+// Description : For reading and writing PFM files using the basic
+//               PNMImage interface, as if they were basic RGB files.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_GRUTIL PNMFileTypePfm : public PNMFileType {
+public:
+  PNMFileTypePfm();
+
+  virtual string get_name() const;
+
+  virtual int get_num_extensions() const;
+  virtual string get_extension(int n) const;
+  virtual string get_suggested_extension() const;
+
+  virtual bool has_magic_number() const;
+  virtual bool matches_magic_number(const string &magic_number) const;
+
+  virtual PNMReader *make_reader(istream *file, bool owns_file = true,
+                                 const string &magic_number = string());
+  virtual PNMWriter *make_writer(ostream *file, bool owns_file = true);
+
+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);
+
+  private:
+    PNMImage _image;
+  };
+
+  class Writer : public PNMWriter {
+  public:
+    Writer(PNMFileType *type, ostream *file, bool owns_file);
+
+    virtual int write_data(xel *array, xelval *alpha);
+  };
+
+
+  // The TypedWritable interface follows.
+public:
+  static void register_with_read_factory();
+
+protected:
+  static TypedWritable *make_PNMFileTypePfm(const FactoryParams &params);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    PNMFileType::init_type();
+    register_type(_type_handle, "PNMFileTypePfm",
+                  PNMFileType::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

+ 2 - 1
pandatool/src/imageprogs/Sources.pp

@@ -2,7 +2,8 @@
   p3imagebase p3progbase
 
 #define OTHER_LIBS \
-    p3pipeline:c p3event:c p3pstatclient:c panda:m \
+    p3pipeline:c p3event:c p3pstatclient:c p3grutil:c \
+    panda:m \
     p3pandabase:c p3pnmimage:c p3pnmimagetypes:c \
     p3mathutil:c p3linmath:c p3putil:c p3express:c \
     pandaexpress:m \