From 7ba693137c518d07ba384c82edb162a2818c4c0c Mon Sep 17 00:00:00 2001 From: David Rose Date: Sun, 12 Aug 2012 15:49:08 +0000 Subject: [PATCH] integrate PfmFile with PNMImage, Texture --- panda/src/gobj/texture.cxx | 326 ++++++++++++++---- panda/src/gobj/texture.h | 12 + panda/src/pnmimage/Sources.pp | 3 - panda/src/pnmimage/config_pnmimage.cxx | 6 - panda/src/pnmimage/p3pnmimage_composite1.cxx | 1 - panda/src/pnmimage/pfmFile.I | 123 ++++--- panda/src/pnmimage/pfmFile.cxx | 225 +++++------- panda/src/pnmimage/pfmFile.h | 25 +- panda/src/pnmimage/pnmImage.cxx | 26 ++ panda/src/pnmimage/pnmReader.cxx | 24 ++ panda/src/pnmimage/pnmReader.h | 4 +- panda/src/pnmimage/pnmWriter.cxx | 37 ++ panda/src/pnmimage/pnmWriter.h | 5 + panda/src/pnmimagetypes/Sources.pp | 2 + .../pnmimagetypes/config_pnmimagetypes.cxx | 5 + .../p3pnmimagetypes_composite2.cxx | 1 + .../pnmFileTypePfm.cxx | 228 ++++++++---- .../pnmFileTypePfm.h | 11 +- panda/src/putil/loaderOptions.h | 2 + 19 files changed, 740 insertions(+), 326 deletions(-) rename panda/src/{pnmimage => pnmimagetypes}/pnmFileTypePfm.cxx (59%) rename panda/src/{pnmimage => pnmimagetypes}/pnmFileTypePfm.h (91%) diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index a4375c2101..a0f2bb6bd5 100644 --- a/panda/src/gobj/texture.cxx +++ b/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 diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index 5b47d492a2..32c272ed25 100644 --- a/panda/src/gobj/texture.h +++ b/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, diff --git a/panda/src/pnmimage/Sources.pp b/panda/src/pnmimage/Sources.pp index 8d1e91319f..76f67ed774 100644 --- a/panda/src/pnmimage/Sources.pp +++ b/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 \ diff --git a/panda/src/pnmimage/config_pnmimage.cxx b/panda/src/pnmimage/config_pnmimage.cxx index 2ea285c79c..add80a36db 100644 --- a/panda/src/pnmimage/config_pnmimage.cxx +++ b/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); } diff --git a/panda/src/pnmimage/p3pnmimage_composite1.cxx b/panda/src/pnmimage/p3pnmimage_composite1.cxx index c7a0716feb..0e290cbc95 100644 --- a/panda/src/pnmimage/p3pnmimage_composite1.cxx +++ b/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" diff --git a/panda/src/pnmimage/pfmFile.I b/panda/src/pnmimage/pfmFile.I index c673f17e8e..2712f17870 100644 --- a/panda/src/pnmimage/pfmFile.I +++ b/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 &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 &table) { + _table.swap(table); +} diff --git a/panda/src/pnmimage/pfmFile.cxx b/panda/src/pnmimage/pfmFile.cxx index 048616e11c..1cc768eb11 100644 --- a/panda/src/pnmimage/pfmFile.cxx +++ b/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 ©) : + 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 ©) : //////////////////////////////////////////////////////////////////// void PfmFile:: operator = (const PfmFile ©) { + 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) { +read(istream &in, const Filename &fullpath) { + PNMReader *reader = make_reader(&in, false, fullpath); + if (reader == (PNMReader *)NULL) { + clear(); + return false; + } + return read(reader); +} + +//////////////////////////////////////////////////////////////////// +// 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(); - string identifier = magic_number; - PNMImageHeader::read_magic_number(&in, identifier, 2); - - 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; + if (!reader->is_valid()) { + delete reader; + return false; + } - } else if (identifier == "Pf") { - _num_channels = 1; - - } 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; - - case 3: - out << "PF\n"; - break; - - case 4: - out << "pf4c\n"; - break; - - default: - nassertr(false, false); - } - out << _x_size << " " << _y_size << "\n"; - - PN_float32 scale = cabs(_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 - out << scale << "\n"; - - int size = _x_size * _y_size * _num_channels; - out.write((const char *)&_table[0], sizeof(PN_float32) * size); - - if (out.fail()) { + if (!is_valid()) { return false; } - nassertr(sizeof(PN_float32) == 4, false); - return true; + + PNMWriter *writer = make_writer(fullpath); + if (writer == (PNMWriter *)NULL) { + return false; + } + + return write(writer); +} + +//////////////////////////////////////////////////////////////////// +// 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; + } + + if (!is_valid()) { + delete writer; + return false; + } + + writer->copy_header_from(*this); + + 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; + } + + 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 diff --git a/panda/src/pnmimage/pfmFile.h b/panda/src/pnmimage/pfmFile.h index df2e0dd7fa..dff4b46557 100644 --- a/panda/src/pnmimage/pfmFile.h +++ b/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 ©); @@ -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 &get_table() const; + INLINE void swap_table(pvector &table); + private: void make_vis_mesh_geom(GeomNode *gnode, bool inverted) const; @@ -196,10 +206,7 @@ private: typedef pvector Table; Table _table; - int _x_size; - int _y_size; PN_float32 _scale; - int _num_channels; bool _has_no_data_value; LPoint4f _no_data_value; diff --git a/panda/src/pnmimage/pnmImage.cxx b/panda/src/pnmimage/pnmImage.cxx index 76a9747031..8e4ed3bd73 100644 --- a/panda/src/pnmimage/pnmImage.cxx +++ b/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++) { diff --git a/panda/src/pnmimage/pnmReader.cxx b/panda/src/pnmimage/pnmReader.cxx index 07558ed390..1397e9c482 100644 --- a/panda/src/pnmimage/pnmReader.cxx +++ b/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 diff --git a/panda/src/pnmimage/pnmReader.h b/panda/src/pnmimage/pnmReader.h index 853a68e004..6d72891210 100644 --- a/panda/src/pnmimage/pnmReader.h +++ b/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); diff --git a/panda/src/pnmimage/pnmWriter.cxx b/panda/src/pnmimage/pnmWriter.cxx index a921572b66..06dc867826 100644 --- a/panda/src/pnmimage/pnmWriter.cxx +++ b/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 diff --git a/panda/src/pnmimage/pnmWriter.h b/panda/src/pnmimage/pnmWriter.h index 732c1bd367..503afcf221 100644 --- a/panda/src/pnmimage/pnmWriter.h +++ b/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; diff --git a/panda/src/pnmimagetypes/Sources.pp b/panda/src/pnmimagetypes/Sources.pp index 1770c44a41..33b27fc712 100644 --- a/panda/src/pnmimagetypes/Sources.pp +++ b/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 \ diff --git a/panda/src/pnmimagetypes/config_pnmimagetypes.cxx b/panda/src/pnmimagetypes/config_pnmimagetypes.cxx index f45201e361..7d864678c4 100644 --- a/panda/src/pnmimagetypes/config_pnmimagetypes.cxx +++ b/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(); diff --git a/panda/src/pnmimagetypes/p3pnmimagetypes_composite2.cxx b/panda/src/pnmimagetypes/p3pnmimagetypes_composite2.cxx index fdae30cad3..7e22a6b6ea 100644 --- a/panda/src/pnmimagetypes/p3pnmimagetypes_composite2.cxx +++ b/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" diff --git a/panda/src/pnmimage/pnmFileTypePfm.cxx b/panda/src/pnmimagetypes/pnmFileTypePfm.cxx similarity index 59% rename from panda/src/pnmimage/pnmFileTypePfm.cxx rename to panda/src/pnmimagetypes/pnmFileTypePfm.cxx index 3077fd9301..d7f002c3cc 100644 --- a/panda/src/pnmimage/pnmFileTypePfm.cxx +++ b/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; } - pfm.store(_image); - PNMImageHeader::operator = (_image); + (*_file) >> _x_size >> _y_size >> _scale; + if (!(*_file)) { + pnmimage_cat.debug() + << "Error parsing PFM header\n"; + _is_valid = false; + return; + } + + // 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; } - nassertr(_image.get_x_size() == get_x_size() && - _image.get_y_size() == get_y_size(), 0); - - memcpy(array, _image.get_array(), get_x_size() * get_y_size() * sizeof(xel)); - - if (has_alpha()) { - memcpy(alpha, _image.get_alpha_array(), get_x_size() * get_y_size() * sizeof(xelval)); + 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; } - return get_y_size(); + 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 table; + pfm.swap_table(table); + + (*_file).read((char *)&table[0], sizeof(PN_float32) * size); + if ((*_file).fail() && !(*_file).eof()) { + pfm.clear(); + 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)); + } + } + + 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. +// 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. //////////////////////////////////////////////////////////////////// -int PNMFileTypePfm::Writer:: -write_data(xel *array, xelval *alpha) { - if (_x_size <= 0 || _y_size <= 0) { - return 0; - } +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; +} - PfmFile pfm; - if (!pfm.load(image)) { - return 0; - } +//////////////////////////////////////////////////////////////////// +// 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); - if (!pfm.write(*_file)) { - return 0; - } + switch (pfm.get_num_channels()) { + case 1: + (*_file) << "Pf\n"; + break; - return get_y_size(); + 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"; + + 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 &table = pfm.get_table(); + (*_file).write((const char *)&table[0], sizeof(PN_float32) * size); + + if ((*_file).fail()) { + return false; + } + nassertr(sizeof(PN_float32) == 4, false); + return true; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pnmimage/pnmFileTypePfm.h b/panda/src/pnmimagetypes/pnmFileTypePfm.h similarity index 91% rename from panda/src/pnmimage/pnmFileTypePfm.h rename to panda/src/pnmimagetypes/pnmFileTypePfm.h index a42da2b405..faf9727496 100644 --- a/panda/src/pnmimage/pnmFileTypePfm.h +++ b/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); }; diff --git a/panda/src/putil/loaderOptions.h b/panda/src/putil/loaderOptions.h index 4be344091e..5d722bc1de 100644 --- a/panda/src/putil/loaderOptions.h +++ b/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);