From 06b2e69a4991b86d9a612815a1e5bfa211c1a821 Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 1 Jul 2014 17:40:37 +0000 Subject: [PATCH] support floating-point tiff files --- panda/src/pnmimage/pfmFile.cxx | 24 +-- panda/src/pnmimagetypes/pnmFileTypePfm.cxx | 2 + panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx | 158 +++++++++++++++++++- panda/src/pnmimagetypes/pnmFileTypeTIFF.h | 6 + 4 files changed, 163 insertions(+), 27 deletions(-) diff --git a/panda/src/pnmimage/pfmFile.cxx b/panda/src/pnmimage/pfmFile.cxx index bae7793e45..b0a4ddd712 100644 --- a/panda/src/pnmimage/pfmFile.cxx +++ b/panda/src/pnmimage/pfmFile.cxx @@ -213,11 +213,10 @@ read(PNMReader *reader) { // 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 other than "pfm"; it -// will be quietly converted to the appropriate integer -// type. +// If the type implied by the filename extension +// supports floating-point, the data will be written +// directly; otherwise, the floating-point data will be +// quietly converted to the appropriate integer type. //////////////////////////////////////////////////////////////////// bool PfmFile:: write(const Filename &fullpath) { @@ -234,21 +233,6 @@ write(const Filename &fullpath) { << "Unable to open " << filename << "\n"; 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 (pnmimage_cat.is_debug()) { pnmimage_cat.debug() diff --git a/panda/src/pnmimagetypes/pnmFileTypePfm.cxx b/panda/src/pnmimagetypes/pnmFileTypePfm.cxx index cc7b5181ff..09623e496a 100644 --- a/panda/src/pnmimagetypes/pnmFileTypePfm.cxx +++ b/panda/src/pnmimagetypes/pnmFileTypePfm.cxx @@ -165,6 +165,8 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) : return; } + _maxval = PGM_MAXMAXVAL; + (*_file) >> _x_size >> _y_size >> _scale; if (!(*_file)) { pnmimage_cat.debug() diff --git a/panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx b/panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx index 5e7655cb18..1461ed2f98 100644 --- a/panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx +++ b/panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx @@ -21,6 +21,7 @@ #include "pnmFileTypeRegistry.h" #include "bamReader.h" #include "ppmcmap.h" +#include "pfmFile.h" // Tiff will want to re-typedef these things. #define int8 tiff_int8 @@ -76,9 +77,6 @@ short tiff_predictor = 0; /* 0, 1, or 2; meaningful when tiff_compression == COMPRESSION_LZW. */ -long tiff_rowsperstrip = 0; -/* 0 or any positive number */ - #ifndef PHOTOMETRIC_DEPTH #define PHOTOMETRIC_DEPTH 32768 #endif @@ -369,6 +367,8 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) : } if (_is_valid) { + if ( ! TIFFGetField( tif, TIFFTAG_SAMPLEFORMAT, &sample_format ) ) + sample_format = SAMPLEFORMAT_UINT; if ( ! TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bps ) ) bps = 1; if ( ! TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &spp ) ) @@ -378,6 +378,20 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) : pnmimage_tiff_cat.error() << "Error getting photometric from TIFF file.\n"; _is_valid = false; + + } else if (sample_format == SAMPLEFORMAT_IEEEFP) { + // Floating-point TIFF. We only accept 32-bit floats. + if (bps != 32) { + pnmimage_tiff_cat.error() + << "Can only read 32-bit float TIFF files, not " << bps << "-bit.\n"; + _is_valid = false; + } + + } else if (sample_format != SAMPLEFORMAT_UINT) { + // Can't understand other kinds of sample formats. + pnmimage_tiff_cat.error() + << "Can't understand sample format\n"; + _is_valid = false; } } @@ -565,6 +579,54 @@ PNMFileTypeTIFF::Reader:: } } +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::Reader::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 PNMFileTypeTIFF::Reader:: +is_floating_point() { + return sample_format = SAMPLEFORMAT_IEEEFP; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::Reader::read_pfm +// Access: Public, Virtual +// Description: Reads floating-point data directly into the indicated +// PfmFile. Returns true on success, false on failure. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeTIFF::Reader:: +read_pfm(PfmFile &pfm) { + if (!is_valid()) { + return false; + } + + int x_size = get_x_size(); + int y_size = get_y_size(); + int num_channels = get_num_channels(); + + pfm.clear(x_size, y_size, num_channels); + vector_float table; + pfm.swap_table(table); + + for (int yi = 0; yi < y_size; ++yi) { + float *row = &table[(yi * x_size) * _num_channels]; + + if (TIFFReadScanline(tif, row, yi, 0 ) < 0 ) { + pnmimage_tiff_cat.error() + << "failed a scanline read on row " << yi << "\n"; + pfm.swap_table(table); + return false; + } + } + + pfm.swap_table(table); + return true; +} + //////////////////////////////////////////////////////////////////// // Function: PNMFileTypeTIFF::Reader::supports_read_row // Access: Public, Virtual @@ -855,6 +917,91 @@ Writer(PNMFileType *type, ostream *file, bool owns_file) : { } +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::Writer::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 PNMFileTypeTIFF::Writer:: +supports_floating_point() { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::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 PNMFileTypeTIFF::Writer:: +supports_integer() { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::Writer::write_pfm +// Access: Public, Virtual +// Description: Writes floating-point data from the indicated +// PfmFile. Returns true on success, false on failure. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeTIFF::Writer:: +write_pfm(const PfmFile &pfm) { + struct tiff *tif; + + // Open output file. + tif = TIFFClientOpen("TIFF file", "w", + (thandle_t) _file, + ostream_dont_read, ostream_write, + (TIFFSeekProc)ostream_seek, + iostream_dont_close, ostream_size, + iostream_map, iostream_unmap); + if (tif == NULL) { + return false; + } + + int x_size = pfm.get_x_size(); + int y_size = pfm.get_y_size(); + int num_channels = pfm.get_num_channels(); + + int photometric = 0; + if (num_channels >= 3) { + photometric = PHOTOMETRIC_RGB; + } + + // Set TIFF parameters. + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, x_size); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, y_size); + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); + TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric); + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + TIFFSetField(tif, TIFFTAG_IMAGEDESCRIPTION, "Generated via pnmimage.\n" ); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, num_channels); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + + const vector_float &table = pfm.get_table(); + for (int yi = 0; yi < y_size; ++yi) { + const float *row = &table[(yi * x_size) * _num_channels]; + + if (TIFFWriteScanline(tif, (tdata_t)row, yi, 0 ) < 0) { + pnmimage_tiff_cat.error() + << "failed a scanline write on row " << yi << "\n"; + return false; + } + } + + TIFFFlushData(tif); + TIFFClose(tif); + + return true; +} + //////////////////////////////////////////////////////////////////// // Function: PNMFileTypeTIFF::Writer::write_data // Access: Public, Virtual @@ -992,8 +1139,6 @@ write_data(xel *array, xelval *alpha) { break; } - if ( tiff_rowsperstrip == 0 ) - tiff_rowsperstrip = ( 8 * 1024 ) / bytesperrow; buf = (unsigned char*) malloc( bytesperrow ); if ( buf == (unsigned char*) 0 ) { pnmimage_tiff_cat.error() @@ -1004,6 +1149,7 @@ write_data(xel *array, xelval *alpha) { /* Set TIFF parameters. */ TIFFSetField( tif, TIFFTAG_IMAGEWIDTH, _x_size ); TIFFSetField( tif, TIFFTAG_IMAGELENGTH, _y_size ); + TIFFSetField( tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT ); TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, bitspersample ); TIFFSetField( tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT ); TIFFSetField( tif, TIFFTAG_COMPRESSION, tiff_compression ); @@ -1020,8 +1166,6 @@ write_data(xel *array, xelval *alpha) { if (has_alpha()) { TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, 1, extra_samples); } - TIFFSetField( tif, TIFFTAG_ROWSPERSTRIP, tiff_rowsperstrip ); - /* TIFFSetField( tif, TIFFTAG_STRIPBYTECOUNTS, _y_size / tiff_rowsperstrip ); */ TIFFSetField( tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ); if ( chv == (colorhist_vector) 0 ) { diff --git a/panda/src/pnmimagetypes/pnmFileTypeTIFF.h b/panda/src/pnmimagetypes/pnmFileTypeTIFF.h index 3f8ecf5e30..07f54faf82 100644 --- a/panda/src/pnmimagetypes/pnmFileTypeTIFF.h +++ b/panda/src/pnmimagetypes/pnmFileTypeTIFF.h @@ -55,6 +55,8 @@ 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 bool supports_read_row() const; virtual bool read_row(xel *array, xelval *alpha, int x_size, int y_size); @@ -65,6 +67,7 @@ public: xelval next_sample_32(unsigned char *&buf_ptr, int &bits_left) const; xelval next_sample_general(unsigned char *&buf_ptr, int &bits_left) const; + unsigned short sample_format; unsigned short photomet; unsigned short bps, spp; unsigned short unassoc_alpha_sample, assoc_alpha_sample; @@ -78,6 +81,9 @@ public: 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); };