diff --git a/modules/imgcodecs/src/grfmt_tiff.cpp b/modules/imgcodecs/src/grfmt_tiff.cpp index 165cdbd164..2760aef73e 100644 --- a/modules/imgcodecs/src/grfmt_tiff.cpp +++ b/modules/imgcodecs/src/grfmt_tiff.cpp @@ -75,6 +75,8 @@ TiffDecoder::TiffDecoder() TIFFSetWarningHandler( GrFmtSilentTIFFErrorHandler ); } m_hdr = false; + m_buf_supported = true; + m_buf_pos = 0; } @@ -115,6 +117,76 @@ ImageDecoder TiffDecoder::newDecoder() const return makePtr(); } +class TiffDecoderBufHelper +{ +public: + static tmsize_t read( thandle_t handle, void* buffer, tmsize_t n ) + { + TiffDecoder *decoder = reinterpret_cast(handle); + const Mat& buf = decoder->m_buf; + const tmsize_t size = buf.cols*buf.rows*buf.elemSize(); + tmsize_t pos = decoder->m_buf_pos; + if ( n > (size - pos) ) + { + n = size - pos; + } + memcpy(buffer, buf.ptr() + pos, n); + decoder->m_buf_pos += n; + return n; + } + + static tmsize_t write( thandle_t /*handle*/, void* /*buffer*/, tmsize_t /*n*/ ) + { + // Not used for decoding. + return 0; + } + + static toff_t seek( thandle_t handle, toff_t offset, int whence ) + { + TiffDecoder *decoder = reinterpret_cast(handle); + const Mat& buf = decoder->m_buf; + const toff_t size = buf.cols*buf.rows*buf.elemSize(); + toff_t new_pos = decoder->m_buf_pos; + switch (whence) + { + case SEEK_SET: + new_pos = offset; + break; + case SEEK_CUR: + new_pos += offset; + break; + case SEEK_END: + new_pos = size + offset; + break; + } + new_pos = std::min(new_pos, size); + decoder->m_buf_pos = (size_t)new_pos; + return new_pos; + } + + static int map( thandle_t handle, void** base, toff_t* size ) + { + TiffDecoder *decoder = reinterpret_cast(handle); + Mat& buf = decoder->m_buf; + *base = buf.ptr(); + *size = buf.cols*buf.rows*buf.elemSize(); + return 0; + } + + static toff_t size( thandle_t handle ) + { + TiffDecoder *decoder = reinterpret_cast(handle); + const Mat& buf = decoder->m_buf; + return buf.cols*buf.rows*buf.elemSize(); + } + + static int close( thandle_t /*handle*/ ) + { + // Do nothing. + return 0; + } +}; + bool TiffDecoder::readHeader() { bool result = false; @@ -124,7 +196,18 @@ bool TiffDecoder::readHeader() { // TIFFOpen() mode flags are different to fopen(). A 'b' in mode "rb" has no effect when reading. // http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html - tif = TIFFOpen(m_filename.c_str(), "r"); + if ( !m_buf.empty() ) + { + m_buf_pos = 0; + tif = TIFFClientOpen( "", "r", reinterpret_cast(this), &TiffDecoderBufHelper::read, + &TiffDecoderBufHelper::write, &TiffDecoderBufHelper::seek, + &TiffDecoderBufHelper::close, &TiffDecoderBufHelper::size, + &TiffDecoderBufHelper::map, /*unmap=*/0 ); + } + else + { + tif = TIFFOpen(m_filename.c_str(), "r"); + } } if( tif ) @@ -472,11 +555,7 @@ bool TiffDecoder::readHdrData(Mat& img) TiffEncoder::TiffEncoder() { m_description = "TIFF Files (*.tiff;*.tif)"; -#ifdef HAVE_TIFF - m_buf_supported = false; -#else m_buf_supported = true; -#endif } TiffEncoder::~TiffEncoder() @@ -509,6 +588,81 @@ void TiffEncoder::writeTag( WLByteStream& strm, TiffTag tag, #ifdef HAVE_TIFF +class TiffEncoderBufHelper +{ +public: + + TiffEncoderBufHelper(std::vector *buf) + : m_buf(buf), m_buf_pos(0) + {} + + TIFF* open () + { + return TIFFClientOpen( "", "w", reinterpret_cast(this), &TiffEncoderBufHelper::read, + &TiffEncoderBufHelper::write, &TiffEncoderBufHelper::seek, + &TiffEncoderBufHelper::close, &TiffEncoderBufHelper::size, + /*map=*/0, /*unmap=*/0 ); + } + + static tmsize_t read( thandle_t /*handle*/, void* /*buffer*/, tmsize_t /*n*/ ) + { + // Not used for encoding. + return 0; + } + + static tmsize_t write( thandle_t handle, void* buffer, tmsize_t n ) + { + TiffEncoderBufHelper *helper = reinterpret_cast(handle); + size_t begin = (size_t)helper->m_buf_pos; + size_t end = begin + n; + if ( helper->m_buf->size() < end ) + { + helper->m_buf->resize(end); + } + memcpy(&(*helper->m_buf)[begin], buffer, n); + helper->m_buf_pos = end; + return n; + } + + static toff_t seek( thandle_t handle, toff_t offset, int whence ) + { + TiffEncoderBufHelper *helper = reinterpret_cast(handle); + const toff_t size = helper->m_buf->size(); + toff_t new_pos = helper->m_buf_pos; + switch (whence) + { + case SEEK_SET: + new_pos = offset; + break; + case SEEK_CUR: + new_pos += offset; + break; + case SEEK_END: + new_pos = size + offset; + break; + } + helper->m_buf_pos = new_pos; + return new_pos; + } + + static toff_t size( thandle_t handle ) + { + TiffEncoderBufHelper *helper = reinterpret_cast(handle); + return helper->m_buf->size(); + } + + static int close( thandle_t /*handle*/ ) + { + // Do nothing. + return 0; + } + +private: + + std::vector* m_buf; + toff_t m_buf_pos; +}; + static void readParam(const std::vector& params, int key, int& value) { for(size_t i = 0; i + 1 < params.size(); i += 2) @@ -559,7 +713,17 @@ bool TiffEncoder::writeLibTiff( const Mat& img, const std::vector& params) // do NOT put "wb" as the mode, because the b means "big endian" mode, not "binary" mode. // http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html - TIFF* pTiffHandle = TIFFOpen(m_filename.c_str(), "w"); + TIFF* pTiffHandle; + + TiffEncoderBufHelper buf_helper(m_buf); + if ( m_buf ) + { + pTiffHandle = buf_helper.open(); + } + else + { + pTiffHandle = TIFFOpen(m_filename.c_str(), "w"); + } if (!pTiffHandle) { return false; @@ -655,7 +819,19 @@ bool TiffEncoder::writeHdr(const Mat& _img) { Mat img; cvtColor(_img, img, COLOR_BGR2XYZ); - TIFF* tif = TIFFOpen(m_filename.c_str(), "w"); + + TIFF* tif; + + TiffEncoderBufHelper buf_helper(m_buf); + if ( m_buf ) + { + tif = buf_helper.open(); + } + else + { + tif = TIFFOpen(m_filename.c_str(), "w"); + } + if (!tif) { return false; @@ -686,8 +862,6 @@ bool TiffEncoder::write( const Mat& img, const std::vector& params) bool TiffEncoder::write( const Mat& img, const std::vector& /*params*/) #endif { - int channels = img.channels(); - int width = img.cols, height = img.rows; int depth = img.depth(); #ifdef HAVE_TIFF if(img.type() == CV_32FC3) @@ -699,6 +873,11 @@ bool TiffEncoder::write( const Mat& img, const std::vector& /*params*/) if (depth != CV_8U && depth != CV_16U) return false; +#ifdef HAVE_TIFF + return writeLibTiff(img, params); +#else + int channels = img.channels(); + int width = img.cols, height = img.rows; int bytesPerChannel = depth == CV_8U ? 1 : 2; int fileStep = width * channels * bytesPerChannel; @@ -711,12 +890,8 @@ bool TiffEncoder::write( const Mat& img, const std::vector& /*params*/) } else { -#ifdef HAVE_TIFF - return writeLibTiff(img, params); -#else if( !strm.open(m_filename) ) return false; -#endif } int rowsPerStrip = (1 << 13)/fileStep; @@ -876,6 +1051,7 @@ bool TiffEncoder::write( const Mat& img, const std::vector& /*params*/) } return true; +#endif } } diff --git a/modules/imgcodecs/src/grfmt_tiff.hpp b/modules/imgcodecs/src/grfmt_tiff.hpp index f019082569..e0caf23a99 100644 --- a/modules/imgcodecs/src/grfmt_tiff.hpp +++ b/modules/imgcodecs/src/grfmt_tiff.hpp @@ -91,6 +91,8 @@ enum TiffFieldType // libtiff based TIFF codec +class TiffDecoderBufHelper; + class TiffDecoder : public BaseImageDecoder { public: @@ -107,10 +109,14 @@ public: ImageDecoder newDecoder() const; protected: + + friend class TiffDecoderBufHelper; + void* m_tif; int normalizeChannelsNumber(int channels) const; bool readHdrData(Mat& img); bool m_hdr; + size_t m_buf_pos; }; #endif