diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 3550774149..e5cef9950e 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -78,7 +78,7 @@ PkgListSet(["PYTHON", "DIRECT", # Python support "VORBIS", "FFMPEG", "SWSCALE", "SWRESAMPLE", # Audio decoding "ODE", "PHYSX", "BULLET", "PANDAPHYSICS", # Physics "SPEEDTREE", # SpeedTree - "ZLIB", "PNG", "JPEG", "TIFF", "SQUISH", "FREETYPE", # 2D Formats support + "ZLIB", "PNG", "JPEG", "TIFF", "OPENEXR", "SQUISH", "FREETYPE", # 2D Formats support ] + MAYAVERSIONS + MAXVERSIONS + [ "FCOLLADA", "ASSIMP", # 3D Formats support "VRPN", "OPENSSL", # Transport "FFTW", # Algorithm helpers @@ -604,6 +604,11 @@ if (COMPILER == "MSVC"): LibName("TIFF", GetThirdpartyDir() + "tiff/lib/libtiff.lib") else: LibName("TIFF", GetThirdpartyDir() + "tiff/lib/tiff.lib") + if (PkgSkip("OPENEXR")==0): + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmImf-2_2.lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread-2_2.lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex-2_2.lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half.lib") if (PkgSkip("JPEG")==0): LibName("JPEG", GetThirdpartyDir() + "jpeg/lib/jpeg-static.lib") if (PkgSkip("ZLIB")==0): LibName("ZLIB", GetThirdpartyDir() + "zlib/lib/zlibstatic.lib") if (PkgSkip("VRPN")==0): LibName("VRPN", GetThirdpartyDir() + "vrpn/lib/vrpn.lib") @@ -778,6 +783,7 @@ if (COMPILER=="GCC"): SmartPkgEnable("OPENAL", "openal", ("openal"), "AL/al.h", framework = "OpenAL") SmartPkgEnable("SQUISH", "", ("squish"), "squish.h") SmartPkgEnable("TIFF", "libtiff-4", ("tiff"), "tiff.h") + SmartPkgEnable("OPENEXR", "", ("openexr"), "ImfOutputFile.h") SmartPkgEnable("VRPN", "", ("vrpn", "quat"), ("vrpn", "quat.h", "vrpn/vrpn_Types.h")) SmartPkgEnable("BULLET", "bullet", ("BulletSoftBody", "BulletDynamics", "BulletCollision", "LinearMath"), ("bullet", "bullet/btBulletDynamicsCommon.h")) SmartPkgEnable("VORBIS", "vorbisfile",("vorbisfile", "vorbis", "ogg"), ("ogg/ogg.h", "vorbis/vorbisfile.h")) @@ -2201,6 +2207,7 @@ DTOOL_CONFIG=[ ("PHAVE_JPEGINT_H", '1', '1'), ("HAVE_VIDEO4LINUX", 'UNDEF', '1'), ("HAVE_TIFF", 'UNDEF', 'UNDEF'), + ("HAVE_OPENEXR", 'UNDEF', 'UNDEF'), ("HAVE_SGI_RGB", '1', '1'), ("HAVE_TGA", '1', '1'), ("HAVE_IMG", '1', '1'), @@ -3824,7 +3831,7 @@ if (not RUNTIME): # if (not RUNTIME): - OPTS=['DIR:panda/src/pnmimagetypes', 'DIR:panda/src/pnmimage', 'BUILDING:PANDA', 'PNG', 'ZLIB', 'JPEG', 'TIFF'] + OPTS=['DIR:panda/src/pnmimagetypes', 'DIR:panda/src/pnmimage', 'BUILDING:PANDA', 'PNG', 'ZLIB', 'JPEG', 'TIFF', 'OPENEXR'] TargetAdd('p3pnmimagetypes_composite1.obj', opts=OPTS, input='p3pnmimagetypes_composite1.cxx') TargetAdd('p3pnmimagetypes_composite2.obj', opts=OPTS, input='p3pnmimagetypes_composite2.cxx') @@ -3869,7 +3876,7 @@ if (not RUNTIME): if (not RUNTIME): OPTS=['DIR:panda/metalibs/panda', 'BUILDING:PANDA', 'JPEG', 'PNG', - 'TIFF', 'ZLIB', 'OPENSSL', 'FREETYPE', 'FFTW', 'ADVAPI', 'WINSOCK2', + 'TIFF', 'OPENEXR', 'ZLIB', 'OPENSSL', 'FREETYPE', 'FFTW', 'ADVAPI', 'WINSOCK2', 'SQUISH', 'NVIDIACG', 'VORBIS', 'WINUSER', 'WINMM', 'WINGDI', 'IPHLPAPI'] TargetAdd('panda_panda.obj', opts=OPTS, input='panda.cxx') diff --git a/panda/src/pnmimagetypes/config_pnmimagetypes.cxx b/panda/src/pnmimagetypes/config_pnmimagetypes.cxx index 4af9eab8e7..c498951f3f 100644 --- a/panda/src/pnmimagetypes/config_pnmimagetypes.cxx +++ b/panda/src/pnmimagetypes/config_pnmimagetypes.cxx @@ -22,6 +22,7 @@ #include "pnmFileTypePNM.h" #include "pnmFileTypePfm.h" #include "pnmFileTypeTIFF.h" +#include "pnmFileTypeEXR.h" #include "pnmFileTypeStbImage.h" #include "sgi.h" @@ -41,6 +42,7 @@ NotifyCategoryDefName(pnmimage_jpg, "jpg", pnmimage_cat); NotifyCategoryDefName(pnmimage_png, "png", pnmimage_cat); NotifyCategoryDefName(pnmimage_pnm, "pnm", pnmimage_cat); NotifyCategoryDefName(pnmimage_tiff, "tiff", pnmimage_cat); +NotifyCategoryDefName(pnmimage_exr, "exr", pnmimage_cat); ConfigVariableEnum sgi_storage_type ("sgi-storage-type", SST_rle, @@ -241,6 +243,12 @@ init_libpnmimagetypes() { tr->register_type(new PNMFileTypeTIFF); #endif +#ifdef HAVE_OPENEXR + PNMFileTypeEXR::init_type(); + PNMFileTypeEXR::register_with_read_factory(); + tr->register_type(new PNMFileTypeEXR); +#endif + #ifdef HAVE_STB_IMAGE PNMFileTypeStbImage::init_type(); PNMFileTypeStbImage::register_with_read_factory(); @@ -259,4 +267,7 @@ init_libpnmimagetypes() { #ifdef HAVE_TIFF ps->add_system("libtiff"); #endif +#ifdef HAVE_OPENEXR + ps->add_system("openexr"); +#endif } diff --git a/panda/src/pnmimagetypes/config_pnmimagetypes.h b/panda/src/pnmimagetypes/config_pnmimagetypes.h index bca20cb449..dca563a683 100644 --- a/panda/src/pnmimagetypes/config_pnmimagetypes.h +++ b/panda/src/pnmimagetypes/config_pnmimagetypes.h @@ -32,6 +32,7 @@ NotifyCategoryDecl(pnmimage_sgi, EXPCL_PANDA_PNMIMAGETYPES, EXPTP_PANDA_PNMIMAGETYPES); NotifyCategoryDecl(pnmimage_tiff, EXPCL_PANDA_PNMIMAGETYPES, EXPTP_PANDA_PNMIMAGETYPES); +NotifyCategoryDecl(pnmimage_exr, EXPCL_PANDA_PNMIMAGETYPES, EXPTP_PANDA_PNMIMAGETYPES); NotifyCategoryDecl(pnmimage_tga, EXPCL_PANDA_PNMIMAGETYPES, EXPTP_PANDA_PNMIMAGETYPES); NotifyCategoryDecl(pnmimage_img, EXPCL_PANDA_PNMIMAGETYPES, EXPTP_PANDA_PNMIMAGETYPES); NotifyCategoryDecl(pnmimage_soft, EXPCL_PANDA_PNMIMAGETYPES, EXPTP_PANDA_PNMIMAGETYPES); diff --git a/panda/src/pnmimagetypes/p3pnmimagetypes_composite1.cxx b/panda/src/pnmimagetypes/p3pnmimagetypes_composite1.cxx index 6f6cee3cf0..97b57a79a8 100644 --- a/panda/src/pnmimagetypes/p3pnmimagetypes_composite1.cxx +++ b/panda/src/pnmimagetypes/p3pnmimagetypes_composite1.cxx @@ -1,8 +1,9 @@ #include "config_pnmimagetypes.cxx" #include "pnmFileTypeBMPReader.cxx" #include "pnmFileTypeBMPWriter.cxx" -#include "pnmFileTypeIMG.cxx" #include "pnmFileTypeBMP.cxx" +#include "pnmFileTypeEXR.cxx" +#include "pnmFileTypeIMG.cxx" #include "pnmFileTypeJPG.cxx" #include "pnmFileTypeJPGReader.cxx" #include "pnmFileTypeJPGWriter.cxx" diff --git a/panda/src/pnmimagetypes/pnmFileTypeEXR.cxx b/panda/src/pnmimagetypes/pnmFileTypeEXR.cxx new file mode 100644 index 0000000000..66ee5c7e91 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeEXR.cxx @@ -0,0 +1,470 @@ +/** + * 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." + * + * @file pnmFileTypeEXR.cxx + * @author drose + * @date 2000-06-19 + */ + +#include "pnmFileTypeEXR.h" + +#ifdef HAVE_OPENEXR + +#include "config_pnmimagetypes.h" + +#include "pnmFileTypeRegistry.h" +#include "bamReader.h" +#include "pfmFile.h" + +#include +#include +#include + +TypeHandle PNMFileTypeEXR::_type_handle; + +static const char * const extensions_exr[] = { + "exr" +}; +static const int num_extensions_exr = sizeof(extensions_exr) / sizeof(const char *); + +// A wrapper class to map OpenEXR's OStream class onto std::ostream. +class ImfStdOstream : public IMF::OStream { +public: + ImfStdOstream(std::ostream &strm) : IMF::OStream("ostream"), _strm(strm) {} + + virtual void write(const char c[/*n*/], int n) { + _strm.write(c, n); + } + + virtual IMF::Int64 tellp() { + return _strm.tellp(); + } + + virtual void seekp(IMF::Int64 pos) { + _strm.seekp(pos); + } + +private: + std::ostream &_strm; +}; + +// A wrapper class to map OpenEXR's IStream class onto std::istream. +class ImfStdIstream : public IMF::IStream { +public: + ImfStdIstream(std::istream &strm, const std::string &magic_number) : IMF::IStream("istream"), _strm(strm) { + // Start by putting back the magic number. + for (std::string::const_reverse_iterator mi = magic_number.rbegin(); + mi != magic_number.rend(); + mi++) { + _strm.putback(*mi); + } + } + + virtual bool isMemoryMapped () const { + return false; + } + + virtual bool read (char c[/*n*/], int n) { + _strm.read(c, n); + if (_strm.gcount() != n) { + throw std::exception(); + } + + bool not_eof = !_strm.eof(); + return not_eof; + } + + virtual IMF::Int64 tellg() { + return _strm.tellg(); + } + + virtual void seekg(IMF::Int64 pos) { + _strm.seekg(pos); + } + + virtual void clear() { + _strm.clear(); + } + +private: + std::istream &_strm; +}; + +PNMFileTypeEXR:: +PNMFileTypeEXR() { +} + +/** + * Returns a few words describing the file type. + */ +string PNMFileTypeEXR:: +get_name() const { + return "OpenEXR"; +} + +/** + * Returns the number of different possible filename extensions associated + * with this particular file type. + */ +int PNMFileTypeEXR:: +get_num_extensions() const { + return num_extensions_exr; +} + +/** + * Returns the nth possible filename extension associated with this particular + * file type, without a leading dot. + */ +string PNMFileTypeEXR:: +get_extension(int n) const { + nassertr(n >= 0 && n < num_extensions_exr, string()); + return extensions_exr[n]; +} + +/** + * 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 PNMFileTypeEXR:: +get_suggested_extension() const { + return "exr"; +} + +/** + * Returns true if this particular file type uses a magic number to identify + * it, false otherwise. + */ +bool PNMFileTypeEXR:: +has_magic_number() const { + return true; +} + +/** + * 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 PNMFileTypeEXR:: +matches_magic_number(const string &magic_number) const { + nassertr(magic_number.size() >= 2, false); + + if (magic_number.size() >= 4) { + // If we have already read all four bytes, use the built-in + // function to check them. + return IMF::isImfMagic(magic_number.data()); + } else { + // Otherwise, check only the first two bytes and call it good enough. + return magic_number[0] == ((IMF::MAGIC >> 0) & 0x00ff) && + magic_number[1] == ((IMF::MAGIC >> 8) & 0x00ff); + } +} + +/** + * 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 *PNMFileTypeEXR:: +make_reader(istream *file, bool owns_file, const string &magic_number) { + init_pnm(); + return new Reader(this, file, owns_file, magic_number); +} + +/** + * 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 *PNMFileTypeEXR:: +make_writer(ostream *file, bool owns_file) { + init_pnm(); + return new Writer(this, file, owns_file); +} + +/** + * + */ +PNMFileTypeEXR::Reader:: +Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) : + PNMReader(type, file, owns_file), + _strm(new ImfStdIstream(*_file, magic_number)), + _imf_file(*_strm) +{ + const IMF::Header &header = _imf_file.header(); + + IMATH_NAMESPACE::Box2i dw = header.dataWindow(); + _x_size = dw.max.x - dw.min.x + 1; + _y_size = dw.max.y - dw.min.y + 1; + + // Find the channels we care about, and ensure they're placed in the + // correct order. + _channel_names.clear(); + + const IMF::ChannelList &channels = header.channels(); + + // Note: including Y in this list allows us to handle grayscale or + // grayscale/alpha images correctly, but also incorrectly detects + // luminance/chroma images as grayscale only. However, these kind + // of images are a pain to handle anyway, so maybe that's OK. + const char *possible_channel_names[] = { "R", "G", "B", "Y", "A", NULL }; + for (const char **pni = possible_channel_names; *pni != NULL; ++pni) { + std::string name = *pni; + IMF::ChannelList::ConstIterator ci = channels.find(name); + if (ci != channels.end()) { + // Found a match. + if (name == "Y" && !_channel_names.empty()) { + // Y is luminance or grayscale. Ignore Y if there are + // already any RGB channels. + } else { + _channel_names.push_back(name); + } + } + } + + if (_channel_names.empty()) { + // Didn't find any channel names that match R, G, B, A, so just + // ask for RGB anyway and trust the OpenEXR library to do the + // right thing. Actually, it just fills them with black, but + // whatever. + _channel_names.push_back("R"); + _channel_names.push_back("G"); + _channel_names.push_back("B"); + } + + _num_channels = (int)_channel_names.size(); + if (_num_channels == 0 || _num_channels > 4) { + _is_valid = false; + return; + } + // We read all OpenEXR files to floating-point, even UINT type, so + // _maxval doesn't matter. But we set it anyway. + _maxval = 65535; + + _is_valid = true; +} + +/** + * + */ +PNMFileTypeEXR::Reader:: +~Reader() { + delete _strm; +} + +/** + * 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 PNMFileTypeEXR::Reader:: +is_floating_point() { + // We read everything to floating-point, since even the UINT type is + // 32 bits, more fidelity than we can represent in our 16-bit + // PNMImage. + return true; +} + +/** + * Reads floating-point data directly into the indicated PfmFile. Returns + * true on success, false on failure. + */ +bool PNMFileTypeEXR::Reader:: +read_pfm(PfmFile &pfm) { + pfm.clear(_x_size, _y_size, _num_channels); + vector_float table; + pfm.swap_table(table); + + PN_float32 *table_data = table.data(); + size_t x_stride = sizeof(PN_float32) * pfm.get_num_channels(); + size_t y_stride = x_stride * pfm.get_x_size(); + nassertr(y_stride * pfm.get_y_size() <= table.size() * sizeof(PN_float32), false); + + const IMF::Header &header = _imf_file.header(); + IMATH_NAMESPACE::Box2i dw = header.dataWindow(); + + IMF::FrameBuffer frameBuffer; + for (int ci = 0; ci < pfm.get_num_channels(); ++ci) { + char *base = (char *)(table_data - (dw.min.x + dw.min.y * pfm.get_x_size()) * pfm.get_num_channels() + ci); + frameBuffer.insert(_channel_names[ci].c_str(), + IMF::Slice(IMF::FLOAT, base, x_stride, y_stride, + 1, 1, 0.0)); + } + + _imf_file.setFrameBuffer(frameBuffer); + + try { + _imf_file.readPixels(dw.min.y, dw.max.y); + } catch (const std::exception &exc) { + pnmimage_exr_cat.error() + << exc.what() << "\n"; + return false; + } + + pfm.swap_table(table); + return true; +} + +/** + * 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 PNMFileTypeEXR::Reader:: +read_data(xel *array, xelval *alpha) { + // This should never come here, since we always read to + // floating-point data. + nassertr(false, 0); + return 0; +} + +/** + * + */ +PNMFileTypeEXR::Writer:: +Writer(PNMFileType *type, ostream *file, bool owns_file) : + PNMWriter(type, file, owns_file) +{ +} + +/** + * 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 PNMFileTypeEXR::Writer:: +supports_floating_point() { + return true; +} + +/** + * 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 PNMFileTypeEXR::Writer:: +supports_integer() { + return false; +} + +/** + * Writes floating-point data from the indicated PfmFile. Returns true on + * success, false on failure. + */ +bool PNMFileTypeEXR::Writer:: +write_pfm(const PfmFile &pfm) { + const vector_float &table = pfm.get_table(); + const PN_float32 *table_data = table.data(); + size_t x_stride = sizeof(PN_float32) * pfm.get_num_channels(); + size_t y_stride = x_stride * pfm.get_x_size(); + nassertr(y_stride * pfm.get_y_size() <= table.size() * sizeof(PN_float32), false); + + const char *channel_names_1[] = { "G" }; + const char *channel_names_2[] = { "G", "A" }; + const char *channel_names_3[] = { "R", "G", "B" }; + const char *channel_names_4[] = { "R", "G", "B", "A" }; + const char **channel_names = NULL; + + switch (pfm.get_num_channels()) { + case 1: + channel_names = channel_names_1; + break; + + case 2: + channel_names = channel_names_2; + break; + + case 3: + channel_names = channel_names_3; + break; + + case 4: + channel_names = channel_names_4; + break; + + default: + return false; + }; + + IMF::Header header(pfm.get_x_size(), pfm.get_y_size()); + for (int ci = 0; ci < pfm.get_num_channels(); ++ci) { + header.channels().insert(channel_names[ci], IMF::Channel(IMF::FLOAT)); + } + + IMF::FrameBuffer frameBuffer; + for (int ci = 0; ci < pfm.get_num_channels(); ++ci) { + const char *base = (const char *)(table_data + ci); + frameBuffer.insert(channel_names[ci], + IMF::Slice(IMF::FLOAT, (char *)base, x_stride, y_stride)); + } + + ImfStdOstream strm(*_file); + IMF::OutputFile file(strm, header); + file.setFrameBuffer(frameBuffer); + + try { + file.writePixels(pfm.get_y_size()); + } catch (const std::exception &exc) { + pnmimage_exr_cat.error() + << exc.what() << "\n"; + return false; + } + + return true; +} + +/** + * 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 PNMFileTypeEXR::Writer:: +write_data(xel *array, xelval *alpha) { + // This should never come here, since we always write to + // floating-point data. + nassertr(false, 0); + return 0; +} + +/** + * Registers the current object as something that can be read from a Bam file. + */ +void PNMFileTypeEXR:: +register_with_read_factory() { + BamReader::get_factory()-> + register_factory(get_class_type(), make_PNMFileTypeEXR); +} + +/** + * 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 *PNMFileTypeEXR:: +make_PNMFileTypeEXR(const FactoryParams ¶ms) { + return PNMFileTypeRegistry::get_global_ptr()->get_type_by_handle(get_class_type()); +} + +#endif // HAVE_OPENEXR diff --git a/panda/src/pnmimagetypes/pnmFileTypeEXR.h b/panda/src/pnmimagetypes/pnmFileTypeEXR.h new file mode 100644 index 0000000000..26b0456bf2 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeEXR.h @@ -0,0 +1,110 @@ +/** + * 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." + * + * @file pnmFileTypeEXR.h + * @author drose + * @date 2000-06-17 + */ + +#ifndef PNMFILETYPEEXR_H +#define PNMFILETYPEEXR_H + +#include "pandabase.h" + +#ifdef HAVE_OPENEXR + +#include "pnmFileType.h" +#include "pnmReader.h" +#include "pnmWriter.h" + +#include +#include + +namespace IMF = OPENEXR_IMF_NAMESPACE; + +class ImfStdIstream; + +/** + * For reading and writing EXR floating-point or integer files. + */ +class EXPCL_PANDA_PNMIMAGETYPES PNMFileTypeEXR : public PNMFileType { +public: + PNMFileTypeEXR(); + + 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 ~Reader(); + + virtual bool is_floating_point(); + virtual bool read_pfm(PfmFile &pfm); + virtual int read_data(xel *array, xelval *alpha); + + private: + class ImfStdIstream *_strm; + IMF::InputFile _imf_file; + + typedef std::vector ChannelNames; + ChannelNames _channel_names; + IMF::PixelType _best_pixel_type; + }; + + class Writer : public PNMWriter { + public: + Writer(PNMFileType *type, ostream *file, bool owns_file); + + virtual bool supports_floating_point(); + virtual bool supports_integer(); + virtual bool write_pfm(const PfmFile &pfm); + virtual int write_data(xel *array, xelval *alpha); + }; + +private: + + // The TypedWritable interface follows. +public: + static void register_with_read_factory(); + +protected: + static TypedWritable *make_PNMFileTypeEXR(const FactoryParams ¶ms); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PNMFileType::init_type(); + register_type(_type_handle, "PNMFileTypeEXR", + 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 // HAVE_OPENEXR + +#endif