diff --git a/3rdparty/libtiff/CMakeLists.txt b/3rdparty/libtiff/CMakeLists.txt index addbb5551..5793021b7 100644 --- a/3rdparty/libtiff/CMakeLists.txt +++ b/3rdparty/libtiff/CMakeLists.txt @@ -6,6 +6,7 @@ project(${TIFF_LIBRARY}) include(CheckFunctionExists) include(CheckIncludeFile) +include(TestBigEndian) check_include_file(assert.h HAVE_ASSERT_H) check_include_file(fcntl.h HAVE_FCNTL_H) @@ -16,6 +17,7 @@ check_include_file(search.h HAVE_SEARCH_H) check_include_file(string.h HAVE_STRING_H) check_include_file(sys/types.h HAVE_SYS_TYPES_H) check_include_file(unistd.h HAVE_UNISTD_H) +test_big_endian(HOST_BIGENDIAN) if(WIN32 AND NOT HAVE_WINRT) set(USE_WIN32_FILEIO 1) diff --git a/3rdparty/libtiff/tif_config.h.cmakein b/3rdparty/libtiff/tif_config.h.cmakein index 182f2833d..55c6cb26a 100644 --- a/3rdparty/libtiff/tif_config.h.cmakein +++ b/3rdparty/libtiff/tif_config.h.cmakein @@ -54,7 +54,7 @@ /* Native cpu byte order: 1 if big-endian (Motorola) or 0 if little-endian (Intel) */ -#define HOST_BIGENDIAN 0 +#define HOST_BIGENDIAN @HOST_BIGENDIAN@ /* Set the native cpu bit order (FILLORDER_LSB2MSB or FILLORDER_MSB2LSB) */ #define HOST_FILLORDER FILLORDER_LSB2MSB diff --git a/modules/highgui/src/grfmt_tiff.cpp b/modules/highgui/src/grfmt_tiff.cpp index 5179531f5..c86b4e365 100644 --- a/modules/highgui/src/grfmt_tiff.cpp +++ b/modules/highgui/src/grfmt_tiff.cpp @@ -111,18 +111,21 @@ bool TiffDecoder::readHeader() bool result = false; close(); - TIFF* tif = TIFFOpen( m_filename.c_str(), "rb" ); + // 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 + TIFF* tif = TIFFOpen( m_filename.c_str(), "r" ); if( tif ) { - int wdth = 0, hght = 0, photometric = 0; + uint wdth = 0, hght = 0; + ushort photometric = 0; m_tif = tif; if( TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &wdth ) && TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &hght ) && TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric )) { - int bpp=8, ncn = photometric > 1 ? 3 : 1; + ushort bpp=8, ncn = photometric > 1 ? 3 : 1; TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp ); TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn ); @@ -175,12 +178,12 @@ bool TiffDecoder::readData( Mat& img ) if( m_tif && m_width && m_height ) { TIFF* tif = (TIFF*)m_tif; - int tile_width0 = m_width, tile_height0 = 0; + uint tile_width0 = m_width, tile_height0 = 0; int x, y, i; int is_tiled = TIFFIsTiled(tif); - int photometric; + ushort photometric; TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric ); - int bpp = 8, ncn = photometric > 1 ? 3 : 1; + ushort bpp = 8, ncn = photometric > 1 ? 3 : 1; TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp ); TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn ); const int bitsPerByte = 8; diff --git a/modules/highgui/test/test_grfmt.cpp b/modules/highgui/test/test_grfmt.cpp index 86954e3e1..edccc0280 100644 --- a/modules/highgui/test/test_grfmt.cpp +++ b/modules/highgui/test/test_grfmt.cpp @@ -408,8 +408,8 @@ TEST(Highgui_Tiff, decode_tile16384x16384) try { - cv::imread(file3); - EXPECT_NO_THROW(cv::imread(file4)); + cv::imread(file3, CV_LOAD_IMAGE_UNCHANGED); + EXPECT_NO_THROW(cv::imread(file4, CV_LOAD_IMAGE_UNCHANGED)); } catch(const std::bad_alloc&) { @@ -419,4 +419,52 @@ TEST(Highgui_Tiff, decode_tile16384x16384) remove(file3.c_str()); remove(file4.c_str()); } + +TEST(Highgui_Tiff, write_read_16bit_big_little_endian) +{ + // see issue #2601 "16-bit Grayscale TIFF Load Failures Due to Buffer Underflow and Endianness" + + // Setup data for two minimal 16-bit grayscale TIFF files in both endian formats + uchar tiff_sample_data[2][86] = { { + // Little endian + 0x49, 0x49, 0x2a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xad, 0xde, 0xef, 0xbe, 0x06, 0x00, 0x00, 0x01, + 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x01, + 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x17, 0x01, 0x04, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 }, { + // Big endian + 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x0c, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x06, 0x01, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, + 0x00, 0x00, 0x01, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x11, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x01, 0x17, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x04 } + }; + + // Test imread() for both a little endian TIFF and big endian TIFF + for (int i = 0; i < 2; i++) + { + string filename = cv::tempfile(".tiff"); + + // Write sample TIFF file + FILE* fp = fopen(filename.c_str(), "wb"); + ASSERT_TRUE(fp != NULL); + ASSERT_EQ((size_t)1, fwrite(tiff_sample_data, 86, 1, fp)); + fclose(fp); + + Mat img = imread(filename, CV_LOAD_IMAGE_UNCHANGED); + + EXPECT_EQ(1, img.rows); + EXPECT_EQ(2, img.cols); + EXPECT_EQ(CV_16U, img.type()); + EXPECT_EQ(sizeof(ushort), img.elemSize()); + EXPECT_EQ(1, img.channels()); + EXPECT_EQ(0xDEAD, img.at(0,0)); + EXPECT_EQ(0xBEEF, img.at(0,1)); + + remove(filename.c_str()); + } +} + #endif