From 7ba47ba3c856e5d1f5f53990d417dd62c0ec3547 Mon Sep 17 00:00:00 2001 From: MetaIO Maintainers Date: Fri, 4 Sep 2026 08:59:10 -0400 Subject: [PATCH] MetaIO 2026-09-04 (1755773c) Code extracted from: https://github.com/Kitware/MetaIO.git at commit 1755773c8d678c5c9a57999c8ec373703134c0e2 (master). --- src/CMakeLists.txt | 25 +- src/localMetaConfiguration.h | 63 ++- src/metaArray.cxx | 80 ++-- src/metaArray.h | 10 +- src/metaArrow.cxx | 161 ++++++-- src/metaArrow.h | 15 +- src/metaBlob.cxx | 6 +- src/metaCommand.cxx | 2 +- src/metaContour.cxx | 12 +- src/metaEllipse.cxx | 2 +- src/metaForm.cxx | 29 +- src/metaForm.h | 10 +- src/metaImage.cxx | 435 ++++++++++++++++---- src/metaImage.h | 84 +++- src/metaLandmark.cxx | 6 +- src/metaLandmark.h | 2 +- src/metaLine.cxx | 6 +- src/metaLine.h | 2 +- src/metaMesh.h | 8 +- src/metaObject.cxx | 513 ++++++++++++++---------- src/metaObject.h | 36 +- src/metaScene.cxx | 94 +++-- src/metaSurface.cxx | 6 +- src/metaSurface.h | 2 +- src/metaUtils.cxx | 116 ++++-- src/metaUtils.h | 8 +- src/tests/CMakeLists.txt | 4 +- src/tests/testMeta13ImageList.cxx | 119 ++++++ src/tests/testMeta14ImageCompressed.cxx | 110 +++++ src/tests/testMeta2Object.cxx | 14 +- src/tests/testMeta4Tube.cxx | 18 +- src/tests/testMeta5Blob.cxx | 4 +- src/tests/testMeta6Surface.cxx | 9 +- src/tests/testMeta7Line.cxx | 13 +- src/tests/testMeta9Landmark.cxx | 4 +- 35 files changed, 1451 insertions(+), 577 deletions(-) create mode 100644 src/tests/testMeta13ImageList.cxx create mode 100644 src/tests/testMeta14ImageCompressed.cxx diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a3535174409..6a5cd2fc33f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,7 +12,7 @@ if(METAIO_FOR_VTK) set(METAIO_NAMESPACE "vtkmetaio") - set(METAIO_TARGET "metaio") + set(METAIO_TARGET "vtkmetaio") set(METAIO_INSTALL_NO_LIBRARIES TRUE) set(METAIO_INSTALL_NO_DEVELOPMENT TRUE) if(BUILD_SHARED_LIBS) @@ -20,8 +20,15 @@ if(METAIO_FOR_VTK) endif() elseif("${METAIO_NAMESPACE}" STREQUAL "ITKMetaIO") set(METAIO_FOR_ITK 1) -elseif(NOT METAIO_NAMESPACE) + if(BUILD_SHARED_LIBS) + add_definitions(-Ditkmetaio_BUILD_SHARED_LIBS) + endif() +elseif("${METAIO_NAMESPACE}" STREQUAL "MetaIO" OR NOT METAIO_NAMESPACE) set(METAIO_NAMESPACE MetaIO) + set(METAIO_FOR_METAIO 1) + if(BUILD_SHARED_LIBS) + add_definitions(-Dmetaio_BUILD_SHARED_LIBS) + endif() endif() if(NOT DEFINED METAIO_TARGET) @@ -117,21 +124,23 @@ if (METAIO_FOR_VTK) SOURCES ${sources} HEADERS ${headers} HEADERS_SUBDIR "vtkmetaio") + vtk_module_set_property(VTK::metaio + PROPERTY DEFINE_SYMBOL + VALUE metaio_EXPORTS) else () add_library(${METAIO_TARGET} ${sources} ${headers} ) + if (METAIO_FOR_ITK) + target_link_libraries(${METAIO_TARGET} PUBLIC + itksys + ) + endif (METAIO_FOR_ITK) endif () include_regular_expression("^.*$") -# Need nsl to resolve gethostbyname on SunOS-5.8 -# and socket also -if(CMAKE_SYSTEM MATCHES "SunOS.*") - target_link_libraries(${METAIO_TARGET} PRIVATE socket nsl) -endif() - target_link_libraries(${METAIO_TARGET} PUBLIC ${METAIO_LIBXML2_LIBRARIES} ${METAIO_ZLIB_LIBRARIES} diff --git a/src/localMetaConfiguration.h b/src/localMetaConfiguration.h index 7b3ee3cd3b7..0229d02f80a 100644 --- a/src/localMetaConfiguration.h +++ b/src/localMetaConfiguration.h @@ -21,26 +21,44 @@ #include "metaIOConfig.h" -#if defined(METAIO_FOR_ITK) || !defined(METAIO_FOR_VTK) +#if defined(METAIO_FOR_ITK) // ITK # define METAIO_USE_NAMESPACE 0 # define METAIO_NAMESPACE ITKMetaIO +# define METAIO_STREAM itksys -# include "itk_zlib.h" +# include +# include # include # include -# define METAIO_EXPORT +# if defined(_WIN32) && defined(itkmetaio_BUILD_SHARED_LIBS) +# ifdef metaio_EXPORTS +# define METAIO_EXPORT __declspec(dllexport) +# define METAIO_EXTERN +# else +# define METAIO_EXPORT __declspec(dllimport) +# define METAIO_EXTERN extern +# endif +# else +# if defined(itkmetaio_BUILD_SHARED_LIBS) +# define METAIO_EXPORT __attribute__((visibility ("default"))) +# else +# define METAIO_EXPORT +# endif +# endif -#else +#elif defined(METAIO_FOR_VTK) // VTK # define METAIO_USE_NAMESPACE 1 # define METAIO_NAMESPACE vtkmetaio +# define METAIO_STREAM vtksys -# include "vtk_zlib.h" +# include +# include # include # include @@ -54,8 +72,39 @@ # define METAIO_EXTERN extern # endif # else -# define METAIO_EXPORT +# if defined(vtkmetaio_BUILD_SHARED_LIBS) +# define METAIO_EXPORT __attribute__((visibility ("default"))) +# else +# define METAIO_EXPORT +# endif +# endif + +#else +// Independent of ITK and VTK + +# define METAIO_USE_NAMESPACE 0 +# define METAIO_NAMESPACE metaio +# define METAIO_STREAM std + +# include "itk_zlib.h" + +# include +# include + +# if defined(_WIN32) && defined(metaio_BUILD_SHARED_LIBS) +# ifdef metaio_EXPORTS +# define METAIO_EXPORT __declspec(dllexport) +# define METAIO_EXTERN +# else +# define METAIO_EXPORT __declspec(dllimport) +# define METAIO_EXTERN extern +# endif +# else +# if defined(metaio_BUILD_SHARED_LIBS) +# define METAIO_EXPORT __attribute__((visibility ("default"))) +# else +# define METAIO_EXPORT +# endif # endif -// end VTK/ITK #endif diff --git a/src/metaArray.cxx b/src/metaArray.cxx index 797fec06592..bcca7dacc88 100644 --- a/src/metaArray.cxx +++ b/src/metaArray.cxx @@ -552,13 +552,9 @@ MetaArray::CanRead(const char * _headerName) const } // Now check the file content - std::ifstream inputStream; + METAIO_STREAM::ifstream inputStream; -#ifdef __sgi - inputStream.open(_headerName, std::ios::in); -#else inputStream.open(_headerName, std::ios::in | std::ios::binary); -#endif if (!inputStream.rdbuf()->is_open()) { @@ -581,13 +577,9 @@ MetaArray::Read(const char * _headerName, bool _readElements, void * _elementDat m_FileName = _headerName; } - auto * tmpStream = new std::ifstream; + auto * tmpStream = new METAIO_STREAM::ifstream; -#ifdef __sgi - tmpStream->open(m_FileName, std::ios::in); -#else - tmpStream->open(m_FileName, std::ios::in | std::ios::binary); -#endif + tmpStream->open(m_FileName.c_str(), std::ios::in | std::ios::binary); if (!tmpStream->rdbuf()->is_open()) { @@ -612,7 +604,7 @@ MetaArray::Read(const char * _headerName, bool _readElements, void * _elementDat bool -MetaArray::CanReadStream(std::ifstream * _stream) const +MetaArray::CanReadStream(METAIO_STREAM::ifstream * _stream) const { if (!strncmp(MET_ReadForm(*_stream).c_str(), "Array", 5)) { @@ -622,7 +614,7 @@ MetaArray::CanReadStream(std::ifstream * _stream) const } bool -MetaArray::ReadStream(std::ifstream * _stream, bool _readElements, void * _elementDataBuffer, bool _autoFreeElementData) +MetaArray::ReadStream(METAIO_STREAM::ifstream * _stream, bool _readElements, void * _elementDataBuffer, bool _autoFreeElementData) { META_DEBUG_PRINT( "MetaArray: ReadStream" ); @@ -671,13 +663,9 @@ MetaArray::ReadStream(std::ifstream * _stream, bool _readElements, void * _eleme { fName = m_ElementDataFileName; } - auto * readStreamTemp = new std::ifstream; + auto * readStreamTemp = new METAIO_STREAM::ifstream; -#ifdef __sgi - readStreamTemp->open(fName, std::ios::in); -#else - readStreamTemp->open(fName, std::ios::binary | std::ios::in); -#endif + readStreamTemp->open(fName.c_str(), std::ios::binary | std::ios::in); if (!readStreamTemp->rdbuf()->is_open()) { std::cout << "MetaArray: Read: Cannot open data file" << '\n'; @@ -756,19 +744,9 @@ MetaArray::Write(const char * _headName, const char * _dataName, bool _writeElem } } - auto * tmpWriteStream = new std::ofstream; + auto * tmpWriteStream = new METAIO_STREAM::ofstream; -// Some older sgi compilers have a error in the ofstream constructor -// that requires a file to exist for output -#ifdef __sgi - { - std::ofstream tFile(m_FileName, std::ios::out); - tFile.close(); - } - tmpWriteStream->open(m_FileName, std::ios::out); -#else - tmpWriteStream->open(m_FileName, std::ios::binary | std::ios::out); -#endif + tmpWriteStream->open(m_FileName.c_str(), std::ios::binary | std::ios::out); if (!tmpWriteStream->rdbuf()->is_open()) { @@ -795,7 +773,7 @@ MetaArray::Write(const char * _headName, const char * _dataName, bool _writeElem } bool -MetaArray::WriteStream(std::ofstream * _stream, bool _writeElements, const void * _constElementData) +MetaArray::WriteStream(METAIO_STREAM::ofstream * _stream, bool _writeElements, const void * _constElementData) { if (m_WriteStream != nullptr) { @@ -989,7 +967,7 @@ MetaArray::M_Read() } bool -MetaArray::M_ReadElements(std::ifstream * _fstream, void * _data, int _dataQuantity) +MetaArray::M_ReadElements(METAIO_STREAM::ifstream * _fstream, void * _data, int _dataQuantity) { META_DEBUG_PRINT( "MetaArray: M_ReadElements" ); @@ -1001,19 +979,27 @@ MetaArray::M_ReadElements(std::ifstream * _fstream, void * _data, int _dataQuant // If compressed we inflate if (m_CompressedData) { - // if m_CompressedElementDataSize is not defined we assume the size of the - // file is the size of the compressed data + // if m_CompressedElementDataSize is not defined we assume the compressed + // data runs from the current position to the end of the file if (m_CompressedElementDataSize == 0) { + const std::streampos dataPos = _fstream->tellg(); _fstream->seekg(0, std::ios::end); - m_CompressedElementDataSize = _fstream->tellg(); - _fstream->seekg(0, std::ios::beg); + m_CompressedElementDataSize = static_cast(_fstream->tellg() - dataPos); + _fstream->seekg(dataPos); } auto * compr = new unsigned char[static_cast(m_CompressedElementDataSize)]; _fstream->read(reinterpret_cast(compr), static_cast(m_CompressedElementDataSize)); - MET_PerformUncompression(compr, m_CompressedElementDataSize, static_cast(_data), readSize); + const bool uncompressed = + MET_PerformUncompression(compr, m_CompressedElementDataSize, static_cast(_data), readSize); + delete[] compr; + if (!uncompressed) + { + std::cerr << "MetaArray: M_ReadElements: could not uncompress element data" << '\n'; + return false; + } } else // if not compressed { @@ -1044,10 +1030,10 @@ MetaArray::M_ReadElements(std::ifstream * _fstream, void * _data, int _dataQuant } bool -MetaArray::M_WriteElements(std::ofstream * _fstream, const void * _data, std::streamoff _dataQuantity) +MetaArray::M_WriteElements(METAIO_STREAM::ofstream * _fstream, const void * _data, std::streamoff _dataQuantity) { bool localData; - std::ofstream * tmpWriteStream; + METAIO_STREAM::ofstream * tmpWriteStream; if (m_ElementDataFileName == "LOCAL") { localData = true; @@ -1056,7 +1042,7 @@ MetaArray::M_WriteElements(std::ofstream * _fstream, const void * _data, std::st else { localData = false; - tmpWriteStream = new std::ofstream; + tmpWriteStream = new METAIO_STREAM::ofstream; std::string dataFileName; std::string pathName; @@ -1070,17 +1056,7 @@ MetaArray::M_WriteElements(std::ofstream * _fstream, const void * _data, std::st dataFileName = m_ElementDataFileName; } -// Some older sgi compilers have a error in the ofstream constructor -// that requires a file to exist for output -#ifdef __sgi - { - std::ofstream tFile(dataFileName, std::ios::out); - tFile.close(); - } - tmpWriteStream->open(dataFileName, std::ios::out); -#else - tmpWriteStream->open(dataFileName, std::ios::binary | std::ios::out); -#endif + tmpWriteStream->open(dataFileName.c_str(), std::ios::binary | std::ios::out); } if (!m_BinaryData) diff --git a/src/metaArray.h b/src/metaArray.h index b50abaf120b..7fd42ed7c66 100644 --- a/src/metaArray.h +++ b/src/metaArray.h @@ -159,10 +159,10 @@ class METAIO_EXPORT MetaArray : public MetaForm bool _autoFreeElementData = false); virtual bool - CanReadStream(std::ifstream * _stream) const; + CanReadStream(METAIO_STREAM::ifstream * _stream) const; virtual bool - ReadStream(std::ifstream * _stream, + ReadStream(METAIO_STREAM::ifstream * _stream, bool _readElements = true, void * _elementDataBuffer = nullptr, bool _autoFreeElementData = false); @@ -174,7 +174,7 @@ class METAIO_EXPORT MetaArray : public MetaForm const void * _constElementData = nullptr); virtual bool - WriteStream(std::ofstream * _stream, bool _writeElements = true, const void * _constElementData = nullptr); + WriteStream(METAIO_STREAM::ofstream * _stream, bool _writeElements = true, const void * _constElementData = nullptr); // PROTECTED protected: @@ -205,10 +205,10 @@ class METAIO_EXPORT MetaArray : public MetaForm M_Read() override; bool - M_ReadElements(std::ifstream * _fstream, void * _data, int _dataQuantity); + M_ReadElements(METAIO_STREAM::ifstream * _fstream, void * _data, int _dataQuantity); bool - M_WriteElements(std::ofstream * _fstream, const void * _data, std::streamoff _dataQuantity); + M_WriteElements(METAIO_STREAM::ofstream * _fstream, const void * _data, std::streamoff _dataQuantity); }; # if (METAIO_USE_NAMESPACE) diff --git a/src/metaArrow.cxx b/src/metaArrow.cxx index da1273c5c5e..0aaefe28770 100644 --- a/src/metaArrow.cxx +++ b/src/metaArrow.cxx @@ -50,7 +50,7 @@ MetaArrow::MetaArrow(unsigned int dim) { META_DEBUG_PRINT( "MetaArrow()" ); MetaArrow::Clear(); - m_NDims = dim; + MetaObject::InitializeEssential(dim); } // @@ -64,11 +64,17 @@ void MetaArrow::PrintInfo() const { MetaObject::PrintInfo(); - std::cout << "Length = " << M_Length << '\n'; + std::cout << "Length = " << m_Length << '\n'; + std::cout << "Position = "; + for (int i = 0; i < m_NDims; i++) + { + std::cout << m_Position[i] << " "; + } + std::cout << '\n'; std::cout << "Direction = "; for (int i = 0; i < m_NDims; i++) { - std::cout << M_Direction[i] << " "; + std::cout << m_Direction[i] << " "; } std::cout << '\n'; } @@ -91,11 +97,16 @@ MetaArrow::CopyInfo(const MetaObject * _object) } if (arrow) { - M_Length = arrow->Length(); + m_Length = arrow->Length(); const double * direction = arrow->Direction(); for (int i = 0; i < m_NDims; i++) { - M_Direction[i] = direction[i]; + m_Direction[i] = direction[i]; + } + const double * position = arrow->Position(); + for (int i = 0; i < m_NDims; i++) + { + m_Position[i] = position[i]; } } } @@ -105,13 +116,13 @@ MetaArrow::CopyInfo(const MetaObject * _object) void MetaArrow::Length(float length) { - M_Length = length; + m_Length = length; } float MetaArrow::Length() const { - return M_Length; + return m_Length; } void @@ -119,14 +130,69 @@ MetaArrow::Direction(const double * direction) { for (int i = 0; i < m_NDims; i++) { - M_Direction[i] = direction[i]; + m_Direction[i] = direction[i]; } } const double * MetaArrow::Direction() const { - return M_Direction; + return m_Direction; +} + +const double * +MetaArrow::Position() const +{ + if (m_APIVersion == 1) + { + return m_Position; + } + else + { + return MetaObject::Position(); + } +} + +double +MetaArrow::Position(int _i) const +{ + if (m_APIVersion == 1) + { + return m_Position[_i]; + } + else + { + return MetaObject::Position(_i); + } +} + +void +MetaArrow::Position(const double * position) +{ + if (m_APIVersion == 1) + { + for (int i = 0; i < m_NDims; i++) + { + m_Position[i] = position[i]; + } + } + else + { + MetaObject::Position(position); + } +} + +void +MetaArrow::Position(int _i, double value) +{ + if (m_APIVersion == 1) + { + m_Position[_i] = value; + } + else + { + MetaObject::Position(_i, value); + } } /** Clear Arrow information */ @@ -138,11 +204,13 @@ MetaArrow::Clear() strcpy(m_ObjectTypeName, "Arrow"); - M_Length = 1; + m_Length = 1; // zero out direction then set to (1,0,0) - memset(M_Direction, 0, 10 * sizeof(double)); - M_Direction[0] = 1.0; + memset(m_Direction, 0, 10 * sizeof(double)); + m_Direction[0] = 1.0; + + memset(m_Position, 0, 10 * sizeof(double)); } /** Set Read fields */ @@ -162,6 +230,10 @@ MetaArrow::M_SetupReadFields() int nDimsRecordNumber = MET_GetFieldRecordNumber("NDims", &m_Fields); + mF = new MET_FieldRecordType; + MET_InitReadField(mF, "Position", MET_DOUBLE_ARRAY, false, nDimsRecordNumber); + m_Fields.push_back(mF); + mF = new MET_FieldRecordType; MET_InitReadField(mF, "Direction", MET_DOUBLE_ARRAY, true, nDimsRecordNumber); mF->terminateRead = true; @@ -176,15 +248,21 @@ MetaArrow::M_SetupWriteFields() MET_FieldRecordType * mF; mF = new MET_FieldRecordType; - MET_InitWriteField(mF, "Length", MET_FLOAT, M_Length); + MET_InitWriteField(mF, "Length", MET_FLOAT, m_Length); m_Fields.push_back(mF); + if (m_APIVersion == 1) + { + mF = new MET_FieldRecordType; + MET_InitWriteField(mF, "Position", MET_DOUBLE_ARRAY, static_cast(m_NDims), m_Position); + m_Fields.push_back(mF); + } + mF = new MET_FieldRecordType; - MET_InitWriteField(mF, "Direction", MET_DOUBLE_ARRAY, static_cast(m_NDims), M_Direction); + MET_InitWriteField(mF, "Direction", MET_DOUBLE_ARRAY, static_cast(m_NDims), m_Direction); m_Fields.push_back(mF); } - bool MetaArrow::M_Read() { @@ -198,20 +276,57 @@ MetaArrow::M_Read() META_DEBUG_PRINT( "MetaArrow: M_Read: Parsing Header" ); - MET_FieldRecordType * mF_length; - mF_length = MET_GetFieldRecord("Length", &m_Fields); - if (mF_length->defined) + MET_FieldRecordType * mF; + mF = MET_GetFieldRecord("Length", &m_Fields); + if (mF && mF->defined) + { + m_Length = static_cast(mF->value[0]); + } + + mF = MET_GetFieldRecord("Position", &m_Fields); + if (mF && mF->defined) { - M_Length = static_cast(mF_length->value[0]); + if (m_APIVersion == 1) + { + for (int i = 0; i < m_NDims; i++) + { + m_Position[i] = mF->value[i]; + } + if (m_FileFormatVersion == 0) + { + for (int i = 0; i < m_NDims; i++) + { + m_Offset[i] = 0; + } + } + } + } + else + { + if (m_FileFormatVersion == 1) + { + std::cout << "MetaArrow: M_Read: Position not found" << '\n'; + return false; + } + else // Old file format + { + if (m_APIVersion == 1) // new API - move offset to position + { + for (int i = 0; i < m_NDims; i++) + { + m_Position[i] = m_Offset[i]; + m_Offset[i] = 0; + } + } + } } - MET_FieldRecordType * mF_direction; - mF_direction = MET_GetFieldRecord("Direction", &m_Fields); - if (mF_direction->defined) + mF = MET_GetFieldRecord("Direction", &m_Fields); + if (mF && mF->defined) { for (int i = 0; i < m_NDims; i++) { - M_Direction[i] = mF_direction->value[i]; + m_Direction[i] = mF->value[i]; } } diff --git a/src/metaArrow.h b/src/metaArrow.h index 7cfdbcf6f73..727f097fa14 100644 --- a/src/metaArrow.h +++ b/src/metaArrow.h @@ -85,6 +85,15 @@ class METAIO_EXPORT MetaArrow : public MetaObject const double * Direction() const; + const double * + Position() const override; + double + Position(int _i) const override; + void + Position(const double * position) override; + void + Position(int _i, double _value) override; + // PROTECTED protected: @@ -97,9 +106,11 @@ class METAIO_EXPORT MetaArrow : public MetaObject bool M_Read() override; - float M_Length{1.0}; // default 1.0 + float m_Length{1.0}; // default 1.0 + + double m_Direction[10]{}; - double M_Direction[10]{}; + double m_Position[10]{}; }; # if (METAIO_USE_NAMESPACE) diff --git a/src/metaBlob.cxx b/src/metaBlob.cxx index cfa402d486a..52ee7b315f9 100644 --- a/src/metaBlob.cxx +++ b/src/metaBlob.cxx @@ -245,20 +245,20 @@ MetaBlob::M_Read() MET_FieldRecordType * mF; mF = MET_GetFieldRecord("NPoints", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { m_NPoints = static_cast(mF->value[0]); } mF = MET_GetFieldRecord("ElementType", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { MET_StringToType(reinterpret_cast(mF->value), &m_ElementType); } mF = MET_GetFieldRecord("PointDim", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { strcpy(m_PointDim, reinterpret_cast(mF->value)); } diff --git a/src/metaCommand.cxx b/src/metaCommand.cxx index c82ebc4d070..731e9779e03 100644 --- a/src/metaCommand.cxx +++ b/src/metaCommand.cxx @@ -1363,7 +1363,7 @@ MetaCommand::ExportGAD(bool dynamic) std::string filename = m_Name; filename += ".gad.xml"; - std::ofstream file; + METAIO_STREAM::ofstream file; #ifdef __sgi file.open(filename.c_str(), std::ios::out); #else diff --git a/src/metaContour.cxx b/src/metaContour.cxx index 498f4864478..b2b55243e54 100644 --- a/src/metaContour.cxx +++ b/src/metaContour.cxx @@ -338,7 +338,7 @@ MetaContour::M_Read() MET_FieldRecordType * mF; mF = MET_GetFieldRecord("Closed", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { if (mF->value[0] != 0.0) { @@ -347,7 +347,7 @@ MetaContour::M_Read() } mF = MET_GetFieldRecord("DisplayOrientation", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { if (mF->value[0] != 0.0) { @@ -356,7 +356,7 @@ MetaContour::M_Read() } mF = MET_GetFieldRecord("PinToSlice", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { if (mF->value[0] != 0.0) { @@ -365,13 +365,13 @@ MetaContour::M_Read() } mF = MET_GetFieldRecord("NControlPoints", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { m_NControlPoints = static_cast(mF->value[0]); } mF = MET_GetFieldRecord("ControlPointDim", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { strcpy(m_ControlPointDim, reinterpret_cast(mF->value)); } @@ -690,7 +690,7 @@ MetaContour::M_Read() delete[] v; char c = ' '; - while ((c != '\n') && (!m_ReadStream->eof())) + while ((c != '\n') && (c != -1) && (!m_ReadStream->eof())) { c = static_cast(m_ReadStream->get()); // to avoid unrecognize charactere } diff --git a/src/metaEllipse.cxx b/src/metaEllipse.cxx index 779d174283e..db60b5cfb15 100644 --- a/src/metaEllipse.cxx +++ b/src/metaEllipse.cxx @@ -188,7 +188,7 @@ MetaEllipse::M_Read() MET_FieldRecordType * mF; mF = MET_GetFieldRecord("Radius", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { for (int i = 0; i < m_NDims; i++) { diff --git a/src/metaForm.cxx b/src/metaForm.cxx index 8ce1224933b..884cfec956a 100644 --- a/src/metaForm.cxx +++ b/src/metaForm.cxx @@ -546,12 +546,8 @@ MetaForm::Read(const char * _fileName) std::cout << "Read FileName = _" << m_FileName << "_" << '\n'; - auto * tmpReadStream = new std::ifstream; -#ifdef __sgi - tmpReadStream->open(m_FileName, std::ios::in); -#else - tmpReadStream->open(m_FileName, std::ios::binary | std::ios::in); -#endif + auto * tmpReadStream = new METAIO_STREAM::ifstream; + tmpReadStream->open(m_FileName.c_str(), std::ios::binary | std::ios::in); if (!tmpReadStream->rdbuf()->is_open()) { @@ -576,7 +572,7 @@ MetaForm::Read(const char * _fileName) } bool -MetaForm::CanReadStream(std::ifstream * _stream) +MetaForm::CanReadStream(METAIO_STREAM::ifstream * _stream) { if (_stream) { @@ -589,14 +585,12 @@ MetaForm::CanReadStream(std::ifstream * _stream) } bool -MetaForm::ReadStream(std::ifstream * _stream) +MetaForm::ReadStream(METAIO_STREAM::ifstream * _stream) { META_DEBUG_PRINT( "MetaForm: ReadStream" ); MetaForm::M_Destroy(); - fflush(nullptr); - Clear(); M_SetupReadFields(); @@ -623,18 +617,9 @@ MetaForm::Write(const char * _fileName) std::cout << "Write FileName = _" << m_FileName << "_" << '\n'; - auto * tmpWriteStream = new std::ofstream; + auto * tmpWriteStream = new METAIO_STREAM::ofstream; -#ifdef __sgi - { - // Create the file. This is required on some older sgi's - std::ofstream tFile(m_FileName, std::ios::out); - tFile.close(); - } - tmpWriteStream->open(m_FileName, std::ios::out); -#else - tmpWriteStream->open(m_FileName, std::ios::binary | std::ios::out); -#endif + tmpWriteStream->open(m_FileName.c_str(), std::ios::binary | std::ios::out); if (!tmpWriteStream->rdbuf()->is_open()) { @@ -653,7 +638,7 @@ MetaForm::Write(const char * _fileName) } bool -MetaForm::WriteStream(std::ofstream * _stream) +MetaForm::WriteStream(METAIO_STREAM::ofstream * _stream) { M_SetupWriteFields(); diff --git a/src/metaForm.h b/src/metaForm.h index 21938c4719f..818c490813d 100644 --- a/src/metaForm.h +++ b/src/metaForm.h @@ -159,23 +159,23 @@ class METAIO_EXPORT MetaForm Read(const char * _fileName = nullptr); static bool - CanReadStream(std::ifstream * _stream) ; + CanReadStream(METAIO_STREAM::ifstream * _stream) ; bool - ReadStream(std::ifstream * _stream); + ReadStream(METAIO_STREAM::ifstream * _stream); bool Write(const char * _fileName = nullptr); bool - WriteStream(std::ofstream * _stream); + WriteStream(METAIO_STREAM::ofstream * _stream); // PROTECTED protected: typedef std::vector FieldsContainerType; - std::ifstream * m_ReadStream; - std::ofstream * m_WriteStream; + METAIO_STREAM::ifstream * m_ReadStream; + METAIO_STREAM::ofstream * m_WriteStream; std::string m_FileName; diff --git a/src/metaImage.cxx b/src/metaImage.cxx index 10a06909234..63599d5395a 100644 --- a/src/metaImage.cxx +++ b/src/metaImage.cxx @@ -35,42 +35,21 @@ namespace { void -openReadStream(std::ifstream & inputStream, const std::string & fname) +openReadStream(METAIO_STREAM::ifstream & inputStream, const std::string & fname) { -#ifdef __sgi - inputStream.open(fname, std::ios::in); -#else - inputStream.open(fname, std::ios::in | std::ios::binary); -#endif + inputStream.open(fname.c_str(), std::ios::in | std::ios::binary); } void -openWriteStream(std::ofstream & outputStream, const std::string & fname, bool append) +openWriteStream(METAIO_STREAM::ofstream & outputStream, const std::string & fname, bool append) { -// Some older sgi compilers have a error in the ofstream constructor -// that requires a file to exist for output -#ifdef __sgi - { - std::ofstream tFile(fname, std::ios::out); - tFile.close(); - } -#endif - if (!append) { -#ifdef __sgi - outputStream.open(fname, std::ios::out); -#else - outputStream.open(fname, std::ios::binary | std::ios::out); -#endif + outputStream.open(fname.c_str(), std::ios::binary | std::ios::out); } else { -#ifdef __sgi - outputStream.open(fname, std::ios::app | std::ios::out); -#else - outputStream.open(fname, std::ios::binary | std::ios::app | std::ios::out); -#endif + outputStream.open(fname.c_str(), std::ios::binary | std::ios::app | std::ios::out); } } @@ -82,7 +61,7 @@ namespace METAIO_NAMESPACE #endif // 1 Gigabyte is the maximum chunk to read/write in on function call -static const std::streamoff MaxIOChunk = 1024 * 1024 * 1024; +constexpr static std::streamoff MaxIOChunk = 1024 * 1024 * 1024; std::set MetaImage::m_ImageReservedKeywords = { "Modality", @@ -318,6 +297,20 @@ MetaImage::PrintInfo() const } std::cout << '\n'; + std::cout << "ElementOrigin = "; + for (i = 0; i < m_NDims; i++) + { + std::cout << m_ElementOrigin[i] << " "; + } + std::cout << '\n'; + + std::cout << "ElementDirection = "; + for (i = 0; i < m_NDims*m_NDims; i++) + { + std::cout << m_ElementDirection[i] << " "; + } + std::cout << '\n'; + std::cout << "ElementSizeValid = " << static_cast(m_ElementSizeValid) << '\n'; std::cout << "ElementSize = "; for (i = 0; i < m_NDims; i++) @@ -385,6 +378,9 @@ MetaImage::CopyInfo(const MetaObject * _object) ElementSize(im->ElementSize()); } + ElementDirection(im->ElementDirection()); + ElementOrigin(im->ElementOrigin()); + ElementMinMaxValid(im->ElementMinMaxValid()); if (im->ElementMinMaxValid()) { @@ -412,6 +408,13 @@ MetaImage::Clear() m_HeaderSize = 0; + memset(m_ElementOrigin, 0, sizeof(m_ElementOrigin)); + memset(m_ElementDirection, 0, sizeof(m_ElementDirection)); + for (int i = 0; i < m_NDims; i++) + { + m_ElementDirection[i*m_NDims+i] = 1; + } + memset(m_SequenceID, 0, sizeof(m_SequenceID)); m_ElementSizeValid = false; @@ -467,7 +470,8 @@ MetaImage::InitializeEssential(int _nDims, MET_ValueEnumType _elementType, int _elementNumberOfChannels, void * _elementData, - bool _allocElementMemory) + bool _allocElementMemory, + bool _initializePosition) { // Only consider at most 10 element of spacing: // See MetaObject::InitializeEssential(_nDims) @@ -478,7 +482,7 @@ MetaImage::InitializeEssential(int _nDims, tmpElementSpacing[i] = static_cast(_elementSpacing[i]); } return InitializeEssential( - _nDims, _dimSize, tmpElementSpacing, _elementType, _elementNumberOfChannels, _elementData, _allocElementMemory); + _nDims, _dimSize, tmpElementSpacing, _elementType, _elementNumberOfChannels, _elementData, _allocElementMemory, _initializePosition); } bool @@ -488,11 +492,19 @@ MetaImage::InitializeEssential(int _nDims, MET_ValueEnumType _elementType, int _elementNumberOfChannels, void * _elementData, - bool _allocElementMemory) + bool _allocElementMemory, + bool _initializePosition) { META_DEBUG_PRINT( "MetaImage: Initialize" ); - MetaObject::InitializeEssential(_nDims); + if (_nDims != m_NDims) + { + // This conditional avoids overwriting _elementSpacing when + // InitializeEssential is called using m_ElementSpacing as + // an argument (that is passed by reference). MetaObject::InitializeEssential + // overwrites m_ElementSpacing with 1s. + MetaObject::InitializeEssential(_nDims); + } int i; if (!m_CompressionTable) @@ -503,7 +515,10 @@ MetaImage::InitializeEssential(int _nDims, } m_SubQuantity[0] = 1; m_Quantity = 1; - m_ElementSizeValid = false; + m_ElementType = _elementType; + m_ElementNumberOfChannels = _elementNumberOfChannels; + + m_ElementSizeValid = true; for (i = 0; i < m_NDims; i++) { m_DimSize[i] = _dimSize[i]; @@ -516,17 +531,28 @@ MetaImage::InitializeEssential(int _nDims, if (m_ElementSize[i] == 0) { m_ElementSize[i] = m_ElementSpacing[i]; + m_ElementSizeValid = false; } - else + } + if (_initializePosition) + { + for (i = 0; i < m_NDims; i++) { - m_ElementSizeValid = true; + m_ElementOrigin[i] = 0; + for (int j = 0; j < m_NDims; j++) + { + if (i != j) + { + m_ElementDirection[i*m_NDims+j] = 0; + } + else + { + m_ElementDirection[i*m_NDims+j] = 1; + } + } } } - m_ElementType = _elementType; - - m_ElementNumberOfChannels = _elementNumberOfChannels; - if (_elementData != nullptr) { m_AutoFreeElementData = false; @@ -680,6 +706,68 @@ MetaImage::ElementSize(int _i, double _value) m_ElementSizeValid = true; } +const double * +MetaImage::ElementOrigin() const +{ + return m_ElementOrigin; +} + +double +MetaImage::ElementOrigin(int _i) const +{ + return m_ElementOrigin[_i]; +} + +void +MetaImage::ElementOrigin(const double * _elementOrigin) +{ + memcpy(m_ElementOrigin, _elementOrigin, m_NDims * sizeof(*m_ElementOrigin)); +} + +void +MetaImage::ElementOrigin(const float * _elementOrigin) +{ + for (int i = 0; i < m_NDims; ++i) + { + m_ElementOrigin[i] = static_cast(_elementOrigin[i]); + } +} + + +void +MetaImage::ElementOrigin(int _i, double _value) +{ + m_ElementOrigin[_i] = _value; +} + +const double * +MetaImage::ElementDirection() const +{ + return m_ElementDirection; +} + +double +MetaImage::ElementDirection(int _i, int _j) const +{ + return m_ElementDirection[_i * m_NDims + _j]; +} + +void +MetaImage::ElementDirection(const double * _direction) +{ + int i; + for (i = 0; i < m_NDims * m_NDims; i++) + { + m_ElementDirection[i] = _direction[i]; + } +} + +void +MetaImage::ElementDirection(int _i, int _j, double _value) +{ + m_ElementDirection[_i * m_NDims + _j] = _value; +} + MET_ValueEnumType MetaImage::ElementType() const { @@ -1111,7 +1199,7 @@ MetaImage::CanRead(const char * _headerName) } // Now check the file content - std::ifstream inputStream; + METAIO_STREAM::ifstream inputStream; openReadStream(inputStream, fname); @@ -1143,7 +1231,7 @@ MetaImage::CanRead(const char * _headerName) bool MetaImage::Read(const char * _headerName, bool _readElements, void * _buffer) { -MetaObject::M_Destroy(); + M_Destroy(); Clear(); @@ -1156,7 +1244,7 @@ MetaObject::M_Destroy(); M_PrepareNewReadStream(); - auto * tmpReadStream = new std::ifstream; + auto * tmpReadStream = new METAIO_STREAM::ifstream; openReadStream(*tmpReadStream, m_FileName); @@ -1181,7 +1269,7 @@ MetaObject::M_Destroy(); } bool -MetaImage::CanReadStream(std::ifstream * _stream) +MetaImage::CanReadStream(METAIO_STREAM::ifstream * _stream) { if (!strncmp(MET_ReadType(*_stream).c_str(), "Image", 5)) { @@ -1192,7 +1280,7 @@ MetaImage::CanReadStream(std::ifstream * _stream) bool -MetaImage::ReadStream(int _nDims, std::ifstream * _stream, bool _readElements, void * _buffer) +MetaImage::ReadStream(int _nDims, METAIO_STREAM::ifstream * _stream, bool _readElements, void * _buffer) { if (!MetaObject::ReadStream(_nDims, _stream)) { @@ -1205,12 +1293,12 @@ MetaImage::ReadStream(int _nDims, std::ifstream * _stream, bool _readElements, v if (_buffer == nullptr) { InitializeEssential( - m_NDims, m_DimSize, m_ElementSpacing, m_ElementType, m_ElementNumberOfChannels, nullptr, true); + m_NDims, m_DimSize, m_ElementSpacing, m_ElementType, m_ElementNumberOfChannels, nullptr, true, false); } else { InitializeEssential( - m_NDims, m_DimSize, m_ElementSpacing, m_ElementType, m_ElementNumberOfChannels, _buffer, false); + m_NDims, m_DimSize, m_ElementSpacing, m_ElementType, m_ElementNumberOfChannels, _buffer, false, false); } int i; @@ -1242,14 +1330,17 @@ MetaImage::ReadStream(int _nDims, std::ifstream * _stream, bool _readElements, v delete[] wrds[i]; } delete[] wrds; - if ((fileImageDim == 0) || (fileImageDim > m_NDims)) + if ((fileImageDim <= 0) || (fileImageDim >= m_NDims)) { - // if optional file dimension size is not given or is larger than - // overall dimension then default to a size of m_NDims - 1. + // if optional file dimension size is not given, is not positive, or + // does not leave a dimension to iterate files over, then default to + // a size of m_NDims - 1. m_DimSize and m_SubQuantity are only + // indexable over [0, m_NDims), so out-of-range values must not reach + // the loops below. fileImageDim = m_NDims - 1; } std::string s; - auto * readStreamTemp = new std::ifstream; + auto * readStreamTemp = new METAIO_STREAM::ifstream; int elementSize; MET_SizeOfType(m_ElementType, &elementSize); elementSize *= m_ElementNumberOfChannels; @@ -1258,6 +1349,7 @@ MetaImage::ReadStream(int _nDims, std::ifstream * _stream, bool _readElements, v { totalFiles *= m_DimSize[i - 1]; } + int filesRead = 0; for (i = 0; i < totalFiles && !_stream->eof(); i++) { std::getline(*_stream, s); @@ -1294,9 +1386,17 @@ MetaImage::ReadStream(int _nDims, std::ifstream * _stream, bool _readElements, v } readStreamTemp->close(); + filesRead++; } } delete readStreamTemp; + if (filesRead < totalFiles) + { + // The list ended early, so the tail of m_ElementData was never read. + std::cerr << "MetaImage: Read: LIST names " << filesRead << " file(s), but " << totalFiles << " are required" + << '\n'; + return false; + } } else if (m_ElementDataFileName.find('%') != std::string::npos) { @@ -1310,7 +1410,7 @@ MetaImage::ReadStream(int _nDims, std::ifstream * _stream, bool _readElements, v int maxV = m_DimSize[m_NDims - 1]; int stepV = 1; std::string s; - auto * readStreamTemp = new std::ifstream; + auto * readStreamTemp = new METAIO_STREAM::ifstream; MET_StringToWordArray(m_ElementDataFileName.c_str(), &nWrds, &wrds); if (nWrds >= 2) { @@ -1422,7 +1522,7 @@ MetaImage::ReadStream(int _nDims, std::ifstream * _stream, bool _readElements, v fName = m_ElementDataFileName; } - auto * readStreamTemp = new std::ifstream; + auto * readStreamTemp = new METAIO_STREAM::ifstream; const char * extensions[] = { "", ".gz", ".Z", nullptr }; for (unsigned ii = 0; extensions[ii] != nullptr; ii++) @@ -1537,7 +1637,7 @@ MetaImage::Write(const char * _headName, } } - auto * tmpWriteStream = new std::ofstream; + auto * tmpWriteStream = new METAIO_STREAM::ofstream; openWriteStream(*tmpWriteStream, m_FileName, _append); @@ -1567,7 +1667,7 @@ MetaImage::Write(const char * _headName, } bool -MetaImage::WriteStream(std::ofstream * _stream, bool _writeElements, const void * _constElementData) +MetaImage::WriteStream(METAIO_STREAM::ofstream * _stream, bool _writeElements, const void * _constElementData) { if (m_WriteStream != nullptr) { @@ -1675,8 +1775,8 @@ MetaImage::WriteROI(int * _indexMin, } // Find the start of the data - auto * readStream = new std::ifstream; - readStream->open(m_FileName, std::ios::binary | std::ios::in); + auto * readStream = new METAIO_STREAM::ifstream; + readStream->open(m_FileName.c_str(), std::ios::binary | std::ios::in); // File must be readable if (!MetaObject::ReadStream(m_NDims, readStream)) @@ -1731,7 +1831,7 @@ MetaImage::WriteROI(int * _indexMin, filename = pathName + filename; } - auto * tmpWriteStream = new std::ofstream; + auto * tmpWriteStream = new METAIO_STREAM::ofstream; tmpWriteStream->open(filename.c_str(), std::ios::binary | std::ios::in | std::ios::out); if (!tmpWriteStream->is_open()) @@ -1752,7 +1852,7 @@ MetaImage::WriteROI(int * _indexMin, { seekoff = seekoff - 1; tmpWriteStream->seekp(dataPos + seekoff, std::ios::beg); - const char zerobyte = 0; + constexpr char zerobyte = 0; tmpWriteStream->write(&zerobyte, 1); } @@ -1843,7 +1943,7 @@ MetaImage::WriteROI(int * _indexMin, } } - auto * tmpWriteStream = new std::ofstream; + auto * tmpWriteStream = new METAIO_STREAM::ofstream; openWriteStream(*tmpWriteStream, m_FileName, _append); @@ -1907,7 +2007,7 @@ MetaImage::WriteROI(int * _indexMin, std::streamoff seekoff = m_Quantity * elementNumberOfBytes; seekoff -= 1; m_WriteStream->seekp(seekoff, std::ios::cur); - const char zerobyte = 0; + constexpr char zerobyte = 0; m_WriteStream->write(&zerobyte, 1); writeResult = M_WriteElementsROI(m_WriteStream, elementData, dataPos, _indexMin, _indexMax); @@ -1927,7 +2027,7 @@ MetaImage::WriteROI(int * _indexMin, } bool -MetaImage::M_WriteElementsROI(std::ofstream * _fstream, +MetaImage::M_WriteElementsROI(METAIO_STREAM::ofstream * _fstream, const void * _data, std::streampos _dataPos, const int * _indexMin, @@ -2038,7 +2138,7 @@ MetaImage::M_ResetValues() delete m_CompressionTable; m_CompressionTable = nullptr; - MetaObject::M_Destroy(); + M_Destroy(); } void @@ -2054,7 +2154,6 @@ MetaImage::M_SetupReadFields() mF = new MET_FieldRecordType; MET_InitReadField(mF, "DimSize", MET_INT_ARRAY, true, nDimsRecNum); - mF->required = true; m_Fields.push_back(mF); mF = new MET_FieldRecordType; @@ -2069,6 +2168,19 @@ MetaImage::M_SetupReadFields() MET_InitReadField(mF, "ImagePosition", MET_FLOAT_ARRAY, false, nDimsRecNum); m_Fields.push_back(mF); + bool fieldRequired = false; + if (m_FileFormatVersion == 1) + { + fieldRequired = true; + } + mF = new MET_FieldRecordType; + MET_InitReadField(mF, "ElementOrigin", MET_FLOAT_ARRAY, fieldRequired, nDimsRecNum); + m_Fields.push_back(mF); + + mF = new MET_FieldRecordType; + MET_InitReadField(mF, "ElementDirection", MET_FLOAT_MATRIX, fieldRequired, nDimsRecNum); + m_Fields.push_back(mF); + mF = new MET_FieldRecordType; MET_InitReadField(mF, "SequenceID", MET_INT_ARRAY, false, nDimsRecNum); m_Fields.push_back(mF); @@ -2089,7 +2201,7 @@ MetaImage::M_SetupReadFields() MET_InitReadField(mF, "ElementSize", MET_FLOAT_ARRAY, false, nDimsRecNum); m_Fields.push_back(mF); - mF = new MET_FieldRecordType; // Set but not used... + mF = new MET_FieldRecordType; // Handled as with DICOM: set, but not used... MET_InitReadField(mF, "ElementNBits", MET_INT, false); m_Fields.push_back(mF); @@ -2136,7 +2248,7 @@ MetaImage::M_SetupWriteFields() if (m_Modality != MET_MOD_UNKNOWN) { mF = new MET_FieldRecordType; - strcpy(s, MET_ValueTypeName[m_Modality]); + strcpy(s, MET_ImageModalityTypeName[m_Modality]); MET_InitWriteField(mF, "Modality", MET_STRING, strlen(s), s); m_Fields.push_back(mF); } @@ -2166,6 +2278,67 @@ MetaImage::M_SetupWriteFields() m_Fields.push_back(mF); } + // Determine if new API was used... + if (m_APIVersion == 0) + { + // Using old API, and writing old format + for (i = 0; i < m_NDims; i++) + { + if (m_ElementOrigin[i] != 0) + { + std::cout << "Warning: ElementOrigin is not supported in MetaIO API version 0" << '\n'; + std::cout << " ElementOrigin will not be written to file." << '\n'; + std::cout << " Use MetaImage::APIVersion to select API version 1 otherwise" << '\n'; + std::cout << " information will be lost." << '\n'; + break; + } + for (int j = 0; j < m_NDims; j++) + { + if ((i != j && m_ElementDirection[i*m_NDims+j] != 0) || + (i == j && m_ElementDirection[i*m_NDims+j] != 1 && m_ElementDirection[i*m_NDims+j] != 0)) + { + std::cout << "Warning: ElementDirection is not supported in MetaIO API version 0" << '\n'; + std::cout << " ElementDirection will not be written to file." << '\n'; + std::cout << " Use MetaImage::APIVersion to select API version 1 otherwise" << '\n'; + std::cout << " information will be lost." << '\n'; + i = m_NDims; + break; + } + } + } + } + + // Dev wants to write old file format + if (m_FileFormatVersion == 0) + { + // Developer used old API + if (m_APIVersion == 0) + { + // Old behavior + // Do not write ElementOrigin or ElementDirection + } + else // Developer used new API, so convert to old file format + { + // Convert to old format + // Will result in loss of ObjectToParent transform, if used in a scene + mF = MET_GetFieldRecord("Offset", &m_Fields); + MET_InitWriteField(mF, "Offset", MET_FLOAT_ARRAY, static_cast(m_NDims), m_ElementOrigin); + + mF = MET_GetFieldRecord("TransformMatrix", &m_Fields); + MET_InitWriteField(mF, "TransformMatrix", MET_FLOAT_MATRIX, static_cast(m_NDims), m_ElementDirection); + } + } + else // Dev wants to Write new file format, so don't change anything + { + mF = new MET_FieldRecordType; + MET_InitWriteField(mF, "ElementOrigin", MET_FLOAT_ARRAY, static_cast(m_NDims), m_ElementOrigin); + m_Fields.push_back(mF); + + mF = new MET_FieldRecordType; + MET_InitWriteField(mF, "ElementDirection", MET_FLOAT_MATRIX, static_cast(m_NDims), m_ElementDirection); + m_Fields.push_back(mF); + } + if (m_ElementMinMaxValid) { mF = new MET_FieldRecordType; @@ -2225,11 +2398,10 @@ MetaImage::M_Read() META_DEBUG_PRINT( "MetaImage: M_Read: Parsing Header" ); MET_FieldRecordType * mF; - META_DEBUG_PRINT( "metaImage: M_Read: elementSpacing[" << 0 << "] = " << m_ElementSpacing[0] ); + int i; mF = MET_GetFieldRecord("DimSize", &m_Fields); if (mF && mF->defined) { - int i; for (i = 0; i < m_NDims; i++) { m_DimSize[i] = static_cast(mF->value[i]); @@ -2252,23 +2424,102 @@ MetaImage::M_Read() mF = MET_GetFieldRecord("SequenceID", &m_Fields); if (mF && mF->defined) { - int i; for (i = 0; i < m_NDims; i++) { m_SequenceID[i] = static_cast(mF->value[i]); } } + // ImagePosition is not written by MetaIO library, so it is probably + // unused unless someone wrote a custom MetaIO writer. Will maintain this + // usage/definition to maintain backward compatibility. mF = MET_GetFieldRecord("ImagePosition", &m_Fields); if (mF && mF->defined) { - int i; for (i = 0; i < m_NDims; i++) { m_Offset[i] = mF->value[i]; } } + if (m_FileFormatVersion == 0) + { + // Old file was read, but developer wants to use the new API, + // so convert old fields to new variables... + if (m_APIVersion == 1) + { + for (i = 0; i < m_NDims; i++) + { + m_ElementOrigin[i] = m_Offset[i]; + m_Offset[i] = 0; + } + for (i = 0; i < m_NDims; i++) + { + for (int j = 0; j < m_NDims; j++) + { + m_ElementDirection[i*m_NDims+j] = m_TransformMatrix[i*m_NDims+j]; + if (i != j) + { + m_TransformMatrix[i*m_NDims+j] = 0; + } + else + { + m_TransformMatrix[i*m_NDims+j] = 1; + } + } + } + } + // otherwise do nothing + } + else // New file was read + { + // New file was read, but developer wants to use the old API, + // so convert new fields to old variables... + // May result in loss of ObjectToParentTransform for images that + // were stored as part of a scene. + if (m_APIVersion == 0) + { + mF = MET_GetFieldRecord("ElementOrigin", &m_Fields); + if (mF && mF->defined) + { + for (i = 0; i < m_NDims; i++) + { + m_Offset[i] = mF->value[i]; + } + } + + mF = MET_GetFieldRecord("ElementDirection", &m_Fields); + if (mF && mF->defined) + { + for (i = 0; i < m_NDims*m_NDims; i++) + { + m_TransformMatrix[i] = mF->value[i]; + } + } + } + else + // New file was read, and developer wants to use the new API, + { + mF = MET_GetFieldRecord("ElementOrigin", &m_Fields); + if (mF && mF->defined) + { + for (i = 0; i < m_NDims; i++) + { + m_ElementOrigin[i] = mF->value[i]; + } + } + + mF = MET_GetFieldRecord("ElementDirection", &m_Fields); + if (mF && mF->defined) + { + for (i = 0; i < m_NDims*m_NDims; i++) + { + m_ElementDirection[i] = mF->value[i]; + } + } + } + } + mF = MET_GetFieldRecord("ElementMin", &m_Fields); if (mF && mF->defined) { @@ -2292,13 +2543,12 @@ MetaImage::M_Read() if (mF && mF->defined) { m_ElementSizeValid = true; - int i; for (i = 0; i < m_NDims; i++) { m_ElementSize[i] = mF->value[i]; } mF = MET_GetFieldRecord("ElementSpacing", &m_Fields); - if (mF && !mF->defined) + if (!mF || !(mF->defined)) { for (i = 0; i < m_NDims; i++) { @@ -2308,7 +2558,6 @@ MetaImage::M_Read() } else { - int i; m_ElementSizeValid = false; for (i = 0; i < m_NDims; i++) { @@ -2345,7 +2594,7 @@ MetaImage::M_Read() } bool -MetaImage::M_ReadElements(std::ifstream * _fstream, void * _data, std::streamoff _dataQuantity) +MetaImage::M_ReadElements(METAIO_STREAM::ifstream * _fstream, void * _data, std::streamoff _dataQuantity) { META_DEBUG_PRINT( "MetaImage: M_ReadElements" ); @@ -2373,15 +2622,16 @@ MetaImage::M_ReadElements(std::ifstream * _fstream, void * _data, std::streamoff // If compressed we inflate if (m_BinaryData && m_CompressedData) { - // if m_CompressedDataSize is not defined we assume the size of the - // file is the size of the compressed data + // if m_CompressedDataSize is not defined we assume the compressed data + // runs from the current position to the end of the file bool compressedDataDeterminedFromFile = false; if (m_CompressedDataSize == 0) { compressedDataDeterminedFromFile = true; + const std::streampos dataPos = _fstream->tellg(); _fstream->seekg(0, std::ios::end); - m_CompressedDataSize = _fstream->tellg(); - _fstream->seekg(0, std::ios::beg); + m_CompressedDataSize = static_cast(_fstream->tellg() - dataPos); + _fstream->seekg(dataPos); } auto * compr = new unsigned char[static_cast(m_CompressedDataSize)]; @@ -2392,7 +2642,8 @@ MetaImage::M_ReadElements(std::ifstream * _fstream, void * _data, std::streamoff return false; } - MET_PerformUncompression(compr, m_CompressedDataSize, static_cast(_data), readSize); + const bool uncompressed = + MET_PerformUncompression(compr, m_CompressedDataSize, static_cast(_data), readSize); if (compressedDataDeterminedFromFile) { @@ -2400,6 +2651,12 @@ MetaImage::M_ReadElements(std::ifstream * _fstream, void * _data, std::streamoff } delete[] compr; + + if (!uncompressed) + { + std::cerr << "MetaImage: M_ReadElements: could not uncompress element data" << '\n'; + return false; + } } else // if not compressed { @@ -2424,7 +2681,7 @@ MetaImage::M_ReadElements(std::ifstream * _fstream, void * _data, std::streamoff } bool -MetaImage::M_WriteElements(std::ofstream * _fstream, const void * _data, std::streamoff _dataQuantity) +MetaImage::M_WriteElements(METAIO_STREAM::ofstream * _fstream, const void * _data, std::streamoff _dataQuantity) { if (m_ElementDataFileName == "LOCAL") @@ -2457,7 +2714,7 @@ MetaImage::M_WriteElements(std::ofstream * _fstream, const void * _data, std::st std::streamoff elementNumberOfBytes = elementSize * m_ElementNumberOfChannels; std::streamoff sliceNumberOfBytes = m_SubQuantity[m_NDims - 1] * elementNumberOfBytes; - auto * writeStreamTemp = new std::ofstream; + auto * writeStreamTemp = new METAIO_STREAM::ofstream; for (i = 1; i <= m_DimSize[m_NDims - 1]; i++) { fName = string_format(dataFileName, i); @@ -2506,7 +2763,7 @@ MetaImage::M_WriteElements(std::ofstream * _fstream, const void * _data, std::st } else // write the image in one unique other file { - auto * writeStreamTemp = new std::ofstream; + auto * writeStreamTemp = new METAIO_STREAM::ofstream; openWriteStream(*writeStreamTemp, dataFileName, false); if (!MetaImage::M_WriteElementData(writeStreamTemp, _data, _dataQuantity)) @@ -2526,7 +2783,7 @@ MetaImage::M_WriteElements(std::ofstream * _fstream, const void * _data, std::st bool -MetaImage::M_WriteElementData(std::ofstream * _fstream, const void * _data, std::streamoff _dataQuantity) +MetaImage::M_WriteElementData(METAIO_STREAM::ofstream * _fstream, const void * _data, std::streamoff _dataQuantity) { if (!m_BinaryData) { @@ -2596,7 +2853,7 @@ MetaImage::ReadROI(int * _indexMin, void * _buffer, unsigned int subSamplingFactor) { -MetaObject::M_Destroy(); + M_Destroy(); Clear(); @@ -2609,7 +2866,7 @@ MetaObject::M_Destroy(); M_PrepareNewReadStream(); - auto * tmpReadStream = new std::ifstream; + auto * tmpReadStream = new METAIO_STREAM::ifstream; openReadStream(*tmpReadStream, m_FileName); @@ -2638,7 +2895,7 @@ bool MetaImage::ReadROIStream(int * _indexMin, int * _indexMax, int _nDims, - std::ifstream * _stream, + METAIO_STREAM::ifstream * _stream, bool _readElements, void * _buffer, unsigned int subSamplingFactor) @@ -2694,7 +2951,7 @@ MetaImage::ReadROIStream(int * _indexMin, } delete[] wrds; char s[1024]; - auto * readStreamTemp = new std::ifstream; + auto * readStreamTemp = new METAIO_STREAM::ifstream; int elementSize; MET_SizeOfType(m_ElementType, &elementSize); elementSize *= m_ElementNumberOfChannels; @@ -2781,7 +3038,7 @@ MetaImage::ReadROIStream(int * _indexMin, int maxV; int stepV = 1; std::string s; - auto * readStreamTemp = new std::ifstream; + auto * readStreamTemp = new METAIO_STREAM::ifstream; MET_StringToWordArray(m_ElementDataFileName.c_str(), &nWrds, &wrds); if (nWrds >= 2) { @@ -2914,7 +3171,7 @@ MetaImage::ReadROIStream(int * _indexMin, fName = m_ElementDataFileName; } - auto * readStreamTemp = new std::ifstream; + auto * readStreamTemp = new METAIO_STREAM::ifstream; const char * extensions[] = { "", ".gz", ".Z", nullptr }; for (unsigned ii = 0; extensions[ii] != nullptr; ii++) @@ -2960,7 +3217,7 @@ MetaImage::ReadROIStream(int * _indexMin, /** Read an ROI */ bool -MetaImage::M_ReadElementsROI(std::ifstream * _fstream, +MetaImage::M_ReadElementsROI(METAIO_STREAM::ifstream * _fstream, void * _data, std::streamoff _dataQuantity, int * _indexMin, @@ -3286,7 +3543,7 @@ MetaImage::M_ReadElementsROI(std::ifstream * _fstream, bool -MetaImage::M_ReadElementData(std::ifstream * _fstream, void * _data, std::streamoff _dataQuantity) +MetaImage::M_ReadElementData(METAIO_STREAM::ifstream * _fstream, void * _data, std::streamoff _dataQuantity) { // NOTE: this method is different from WriteElementData std::streamoff gc = 0; diff --git a/src/metaImage.h b/src/metaImage.h index 6a7d3afd42a..736ea219fe6 100644 --- a/src/metaImage.h +++ b/src/metaImage.h @@ -122,7 +122,8 @@ class METAIO_EXPORT MetaImage : public MetaObject MET_ValueEnumType _elementType, int _elementNumberOfChannels = 1, void * _elementData = nullptr, - bool _allocElementMemory = true); + bool _allocElementMemory = true, + bool _initializePosition = true); bool InitializeEssential(int _nDims, @@ -131,7 +132,8 @@ class METAIO_EXPORT MetaImage : public MetaObject MET_ValueEnumType _elementType, int _elementNumberOfChannels = 1, void * _elementData = nullptr, - bool _allocElementMemory = true); + bool _allocElementMemory = true, + bool _initializePosition = true); int HeaderSize() const; @@ -251,6 +253,60 @@ class METAIO_EXPORT MetaImage : public MetaObject void ElementToIntensityFunctionOffset(double _elementOffset); + // (Copied from ITK's definition of Direction and Origin) + // + // The position and orientation of an image is defined by its "Origin" + // and its "Directions". The "Origin" is the physical position of the + // pixel whose "Index" is all zeros. The "Direction" of an image is a + // matrix whose columns indicate the direction in physical space that + // each dimension of the image traverses. The first column defines the + // direction that the fastest moving index in the image traverses in + // physical space while the last column defines the direction that the + // slowest moving index in the image traverses in physical space. + // + // Set the direction cosines of the image. The direction cosines + // are vectors that point from one pixel to the next. + // + // Each column of the matrix indicates the direction cosines of the unit vector + // that is parallel to the lines of the image grid corresponding to that + // dimension. For example, an image with Direction matrix + // + // 0.866 0.500 + // -0.500 0.866 + // + // has an image grid were the fastest changing index (dimension[0]) walks + // over a line that in physical space is oriented parallel to the vector + // (0.866, -0.5). The second fastest changing index (dimension[1]) walks along + // a line that in Physical space is oriented parallel to the vector + // (0.5, 0.866) + // + // The columns of the Direction matrix are expected to form an + // orthogonal right handed coordinate system. But this is not + // checked nor enforced in itk::ImageBase. + // + // For details, please see: + // + // https://www.itk.org/Wiki/Proposals:Orientation#Some_notes_on_the_DICOM_convention_and_current_ITK_usage + const double * + ElementOrigin() const; + double + ElementOrigin(int _i) const; + void + ElementOrigin(const double * _position); + void + ElementOrigin(const float * _position); + void + ElementOrigin(int _i, double _value); + + const double * + ElementDirection() const; + double + ElementDirection(int _i, int _j) const; + void + ElementDirection(const double * _direction); + void + ElementDirection(int _i, int _j, double _value); + bool AutoFreeElementData() const; void @@ -303,16 +359,16 @@ class METAIO_EXPORT MetaImage : public MetaObject static bool - CanReadStream(std::ifstream * _stream) ; + CanReadStream(METAIO_STREAM::ifstream * _stream) ; bool - ReadStream(int _nDims, std::ifstream * _stream, bool _readElements = true, void * _buffer = nullptr); + ReadStream(int _nDims, METAIO_STREAM::ifstream * _stream, bool _readElements = true, void * _buffer = nullptr); bool ReadROIStream(int * _indexMin, int * _indexMax, int _nDims, - std::ifstream * _stream, + METAIO_STREAM::ifstream * _stream, bool _readElements = true, void * _buffer = nullptr, unsigned int subSamplingFactor = 1); @@ -340,7 +396,7 @@ class METAIO_EXPORT MetaImage : public MetaObject bool _append = false); bool - WriteStream(std::ofstream * _stream, bool _writeElements = true, const void * _constElementData = nullptr); + WriteStream(METAIO_STREAM::ofstream * _stream, bool _writeElements = true, const void * _constElementData = nullptr); bool @@ -380,6 +436,9 @@ class METAIO_EXPORT MetaImage : public MetaObject double m_ElementToIntensityFunctionSlope{}; double m_ElementToIntensityFunctionOffset{}; + double m_ElementOrigin[10]{}; // "ElementOrigin = " 0,0,0 + double m_ElementDirection[100]{}; // "ElementDirection = " 1,0,0,0,1,0,0,0,1 + bool m_AutoFreeElementData{}; void * m_ElementData{}; @@ -402,13 +461,13 @@ class METAIO_EXPORT MetaImage : public MetaObject // _dataQuantity is expressed in number of pixels. Internally it will be // scaled by the number of components and number of bytes per component. bool - M_ReadElements(std::ifstream * _fstream, void * _data, std::streamoff _dataQuantity); + M_ReadElements(METAIO_STREAM::ifstream * _fstream, void * _data, std::streamoff _dataQuantity); // _totalDataQuantity and _dataQuantity are expressed in number of pixels. // Internally they will be scaled by the number of components and number of // bytes per component. bool - M_ReadElementsROI(std::ifstream * _fstream, + M_ReadElementsROI(METAIO_STREAM::ifstream * _fstream, void * _data, std::streamoff _dataQuantity, int * _indexMin, @@ -417,20 +476,20 @@ class METAIO_EXPORT MetaImage : public MetaObject std::streamoff _totalDataQuantity = 0); bool - M_ReadElementData(std::ifstream * _fstream, void * _data, std::streamoff _dataQuantity); + M_ReadElementData(METAIO_STREAM::ifstream * _fstream, void * _data, std::streamoff _dataQuantity); bool - M_WriteElements(std::ofstream * _fstream, const void * _data, std::streamoff _dataQuantity); + M_WriteElements(METAIO_STREAM::ofstream * _fstream, const void * _data, std::streamoff _dataQuantity); bool - M_WriteElementsROI(std::ofstream * _fstream, + M_WriteElementsROI(METAIO_STREAM::ofstream * _fstream, const void * _data, std::streampos _dataPos, const int * _indexMin, const int * _indexMax); bool - M_WriteElementData(std::ofstream * _fstream, const void * _data, std::streamoff _dataQuantity); + M_WriteElementData(METAIO_STREAM::ofstream * _fstream, const void * _data, std::streamoff _dataQuantity); static bool M_FileExists(const char * filename) ; @@ -450,6 +509,7 @@ class METAIO_EXPORT MetaImage : public MetaObject MET_ValueEnumType _elementType, int _elementNumberOfChannels, void * _elementData); + }; # if (METAIO_USE_NAMESPACE) diff --git a/src/metaLandmark.cxx b/src/metaLandmark.cxx index 3da80fb7140..fec57bd077b 100644 --- a/src/metaLandmark.cxx +++ b/src/metaLandmark.cxx @@ -245,20 +245,20 @@ MetaLandmark::M_Read() MET_FieldRecordType * mF; mF = MET_GetFieldRecord("NPoints", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { m_NPoints = static_cast(mF->value[0]); } mF = MET_GetFieldRecord("ElementType", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { MET_StringToType(reinterpret_cast(mF->value), &m_ElementType); } mF = MET_GetFieldRecord("PointDim", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { strcpy(m_PointDim, reinterpret_cast(mF->value)); } diff --git a/src/metaLandmark.h b/src/metaLandmark.h index 59082e1a4c8..603b5d396c7 100644 --- a/src/metaLandmark.h +++ b/src/metaLandmark.h @@ -43,7 +43,7 @@ namespace METAIO_NAMESPACE { # endif -class LandmarkPnt +class METAIO_EXPORT LandmarkPnt { public: explicit LandmarkPnt(int dim); diff --git a/src/metaLine.cxx b/src/metaLine.cxx index 91abb87436d..37a7b4aae70 100644 --- a/src/metaLine.cxx +++ b/src/metaLine.cxx @@ -255,19 +255,19 @@ MetaLine::M_Read() MET_FieldRecordType * mF; mF = MET_GetFieldRecord("NPoints", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { m_NPoints = static_cast(mF->value[0]); } mF = MET_GetFieldRecord("ElementType", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { MET_StringToType(reinterpret_cast(mF->value), &m_ElementType); } mF = MET_GetFieldRecord("PointDim", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { strcpy(m_PointDim, reinterpret_cast(mF->value)); } diff --git a/src/metaLine.h b/src/metaLine.h index efaa6e5ec3b..bae4e4695ef 100644 --- a/src/metaLine.h +++ b/src/metaLine.h @@ -40,7 +40,7 @@ namespace METAIO_NAMESPACE { # endif -class LinePnt +class METAIO_EXPORT LinePnt { public: explicit LinePnt(int dim); diff --git a/src/metaMesh.h b/src/metaMesh.h index 66bd02cdf0d..8545af6664c 100644 --- a/src/metaMesh.h +++ b/src/metaMesh.h @@ -115,7 +115,7 @@ class METAIO_EXPORT MeshDataBase virtual ~MeshDataBase() = default; virtual void - Write(std::ofstream * stream) = 0; + Write(METAIO_STREAM::ofstream * stream) = 0; virtual unsigned int GetSize() = 0; virtual MET_ValueEnumType @@ -123,8 +123,8 @@ class METAIO_EXPORT MeshDataBase int m_Id; protected: - std::ifstream * m_ReadStream{}; - std::ofstream * m_WriteStream{}; + METAIO_STREAM::ifstream * m_ReadStream{}; + METAIO_STREAM::ofstream * m_WriteStream{}; }; /** Mesh point data class for basic types (i.e int, float ... ) */ @@ -142,7 +142,7 @@ class METAIO_EXPORT MeshData : public MeshDataBase } void - Write(std::ofstream * stream) override + Write(METAIO_STREAM::ofstream * stream) override { // char* id = new char[sizeof(int)]; // The file is written as LSB by default diff --git a/src/metaObject.cxx b/src/metaObject.cxx index c105433ba07..07c3b1559e9 100644 --- a/src/metaObject.cxx +++ b/src/metaObject.cxx @@ -31,13 +31,16 @@ namespace METAIO_NAMESPACE MetaObject::MetaObject() { m_NDims = 0; - this->ClearFields(); - this->ClearUserFields(); - this->ClearAdditionalFields(); + m_Fields.clear(); + m_UserDefinedReadFields.clear(); + m_UserDefinedWriteFields.clear(); + m_AdditionalReadFields.clear(); MetaObject::Clear(); m_ReadStream = nullptr; m_WriteStream = nullptr; - m_FileName[0] = '\0'; + m_FileFormatVersion = 0; + m_APIVersion = 0; + m_FileName = ""; m_Event = nullptr; m_DoublePrecision = METAIO_MAX_DIGITS10; m_DistanceUnits = MET_DISTANCE_UNITS_UNKNOWN; @@ -46,9 +49,10 @@ MetaObject::MetaObject() MetaObject::MetaObject(const char * _fileName) { m_NDims = 0; - this->ClearFields(); - this->ClearUserFields(); - this->ClearAdditionalFields(); + m_Fields.clear(); + m_UserDefinedReadFields.clear(); + m_UserDefinedWriteFields.clear(); + m_AdditionalReadFields.clear(); MetaObject::Clear(); m_ReadStream = nullptr; m_WriteStream = nullptr; @@ -61,14 +65,17 @@ MetaObject::MetaObject(const char * _fileName) MetaObject::MetaObject(unsigned int dim) { m_NDims = 0; - this->ClearFields(); - this->ClearUserFields(); - this->ClearAdditionalFields(); - MetaObject::Clear(); + m_Fields.clear(); + m_UserDefinedReadFields.clear(); + m_UserDefinedWriteFields.clear(); + m_AdditionalReadFields.clear(); m_ReadStream = nullptr; m_WriteStream = nullptr; + m_FileFormatVersion = 0; + m_APIVersion = 0; m_FileName[0] = '\0'; InitializeEssential(dim); + MetaObject::Clear(); m_Event = nullptr; m_DoublePrecision = METAIO_MAX_DIGITS10; m_DistanceUnits = MET_DISTANCE_UNITS_UNKNOWN; @@ -77,7 +84,7 @@ MetaObject::MetaObject(unsigned int dim) MetaObject::~MetaObject() { -MetaObject::M_Destroy(); + MetaObject::M_Destroy(); delete m_ReadStream; delete m_WriteStream; @@ -163,10 +170,20 @@ MetaObject ::ClearUserFields() { MET_FieldRecordType * field = *it; ++it; + auto it2 = m_Fields.begin(); + auto end2 = m_Fields.end(); + while (it2 != end2) + { + if (*it2 == field) + { + m_Fields.erase(it2); + break; + } + ++it2; + } delete field; } - // Clear read field it = m_UserDefinedReadFields.begin(); end = m_UserDefinedReadFields.end(); @@ -192,6 +209,17 @@ MetaObject ::ClearUserFields() if (!deleted) { + auto fieldit2 = m_Fields.begin(); + auto fieldend2 = m_Fields.end(); + while (fieldit2 != fieldend2) + { + if (*fieldit2 == field) + { + m_Fields.erase(fieldit2); + break; + } + ++fieldit2; + } delete field; } } @@ -217,6 +245,30 @@ MetaObject ::ClearAdditionalFields() m_AdditionalReadFields.clear(); } +void +MetaObject::FileFormatVersion(unsigned int _fileFormatVersion) +{ + m_FileFormatVersion = _fileFormatVersion; +} + +unsigned int +MetaObject::FileFormatVersion() const +{ + return m_FileFormatVersion; +} + +void +MetaObject::APIVersion(unsigned int _APIVersion) +{ + m_APIVersion = _APIVersion; +} + +unsigned int +MetaObject::APIVersion() const +{ + return m_APIVersion; +} + void MetaObject::FileName(const char * _fileName) { @@ -243,6 +295,8 @@ MetaObject::CopyInfo(const MetaObject * _object) std::cout << "MetaObject: CopyInfo: Warning: NDims not same size" << '\n'; } + FileFormatVersion(_object->FileFormatVersion()); + APIVersion(_object->APIVersion()); FileName(_object->FileName()); Comment(_object->Comment()); ObjectTypeName(_object->ObjectTypeName()); @@ -271,13 +325,9 @@ MetaObject::Read(const char * _fileName) m_FileName = _fileName; } - auto * tmpReadStream = new std::ifstream; + auto * tmpReadStream = new METAIO_STREAM::ifstream; -#ifdef __sgi - tmpReadStream->open(m_FileName, std::ios::in); -#else - tmpReadStream->open(m_FileName, std::ios::binary | std::ios::in); -#endif + tmpReadStream->open(m_FileName.c_str(), std::ios::binary | std::ios::in); if (!tmpReadStream->rdbuf()->is_open()) { @@ -301,13 +351,11 @@ MetaObject::Read(const char * _fileName) bool -MetaObject::ReadStream(int _nDims, std::ifstream * _stream) +MetaObject::ReadStream(int _nDims, METAIO_STREAM::ifstream * _stream) { META_DEBUG_PRINT( "MetaObject: ReadStream" ); -MetaObject::M_Destroy(); - - fflush(nullptr); + MetaObject::M_Destroy(); Clear(); @@ -344,17 +392,10 @@ MetaObject::Write(const char * _fileName) if (!m_WriteStream) { - m_WriteStream = new std::ofstream; + m_WriteStream = new METAIO_STREAM::ofstream; } -#ifdef __sgi - // Create the file. This is required on some older sgi's - std::ofstream tFile(m_FileName, std::ios::out); - tFile.close(); - m_WriteStream->open(m_FileName, std::ios::out); -#else - m_WriteStream->open(m_FileName, std::ios::binary | std::ios::out); -#endif + m_WriteStream->open(m_FileName.c_str(), std::ios::binary | std::ios::out); if (!m_WriteStream->rdbuf()->is_open()) { @@ -376,10 +417,12 @@ MetaObject::PrintInfo() const int i; int j; - std::cout << "FileName = _" << m_FileName << "_" << '\n'; - std::cout << "Comment = _" << m_Comment << "_" << '\n'; std::cout << "ObjectType = _" << m_ObjectTypeName << "_" << '\n'; std::cout << "ObjectSubType = _" << m_ObjectSubTypeName << "_" << '\n'; + std::cout << "FileFormatVersion = " << m_FileFormatVersion << '\n'; + std::cout << "APIVersion = " << m_APIVersion << '\n'; + std::cout << "FileName = _" << m_FileName << "_" << '\n'; + std::cout << "Comment = _" << m_Comment << "_" << '\n'; std::cout << "NDims = " << m_NDims << '\n'; std::cout << "Name = " << m_Name << '\n'; std::cout << "ID = " << m_ID << '\n'; @@ -453,60 +496,30 @@ MetaObject::PrintInfo() const std::cout << "DistanceUnits = " << this->DistanceUnitsName() << '\n'; // Print User's fields : - auto itw = m_UserDefinedWriteFields.begin(); - auto itr = m_UserDefinedReadFields.begin(); - auto endw = m_UserDefinedWriteFields.end(); - FieldsContainerType::const_iterator it; + auto itw = m_UserDefinedWriteFields.begin(); + auto endw = m_UserDefinedWriteFields.end(); + std::cout << "UserDefinedWriteFields:" << '\n' + << "Number of fields = " << m_UserDefinedWriteFields.size() << '\n'; + std::cout << "***" << '\n'; while (itw != endw) { - if ((*itw)->defined) - { - it = itw; - } - else - { - it = itr; - } - - printf("%s: ", (*it)->name); - - if ((*it)->type == MET_STRING) - { - printf("%s", reinterpret_cast((*it)->value)); - } - else if ((*it)->type == MET_ASCII_CHAR || (*it)->type == MET_CHAR || (*it)->type == MET_UCHAR || - (*it)->type == MET_SHORT || (*it)->type == MET_USHORT || (*it)->type == MET_LONG || - (*it)->type == MET_ULONG || (*it)->type == MET_INT || (*it)->type == MET_UINT || - (*it)->type == MET_FLOAT || (*it)->type == MET_DOUBLE) - { - printf("%s : %f\n", (*it)->name, (*it)->value[0]); - } - else if ((*it)->type == MET_CHAR_ARRAY || (*it)->type == MET_UCHAR_ARRAY || (*it)->type == MET_SHORT_ARRAY || - (*it)->type == MET_USHORT_ARRAY || (*it)->type == MET_INT_ARRAY || (*it)->type == MET_UINT_ARRAY || - (*it)->type == MET_FLOAT_ARRAY || (*it)->type == MET_DOUBLE_ARRAY) - { - for (i = 0; i < (*it)->length; i++) - { - printf("%f ", (*it)->value[i]); - } - } - else if ((*it)->type == MET_FLOAT_MATRIX) - { - std::cout << '\n'; - for (i = 0; i < (*it)->length * (*it)->length; i++) - { - printf("%f ", (*it)->value[i]); - if (i == (*it)->length - 1) - { - std::cout << '\n'; - } - } - } - std::cout << '\n'; - + MET_PrintFieldRecord(std::cout, *itw); + std::cout << "***" << '\n'; ++itw; + } + + auto itr = m_UserDefinedReadFields.begin(); + auto endr = m_UserDefinedReadFields.end(); + std::cout << "UserDefinedReadFields:" << '\n' + << "Number of fields = " << m_UserDefinedReadFields.size() << '\n'; + std::cout << "***" << '\n'; + while (itr != endr) + { + MET_PrintFieldRecord(std::cout, *itr); + std::cout << "***" << '\n'; ++itr; } + } const char * @@ -583,18 +596,30 @@ MetaObject::Offset(int _i, double _value) const double * MetaObject::Position() const { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Position is deprecated, please use Offset" << '\n'; + } return m_Offset; } double MetaObject::Position(int _i) const { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Position is deprecated, please use Offset" << '\n'; + } return m_Offset[_i]; } void MetaObject::Position(const double * _position) { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Position is deprecated, please use Offset" << '\n'; + } int i; for (i = 0; i < m_NDims; i++) { @@ -605,24 +630,40 @@ MetaObject::Position(const double * _position) void MetaObject::Position(int _i, double _value) { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Position is deprecated, please use Offset" << '\n'; + } m_Offset[_i] = _value; } const double * MetaObject::Origin() const { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Origin is deprecated, please use Offset" << '\n'; + } return m_Offset; } double MetaObject::Origin(int _i) const { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Origin is deprecated, please use Offset" << '\n'; + } return m_Offset[_i]; } void MetaObject::Origin(const double * _position) { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Origin is deprecated, please use Offset" << '\n'; + } int i; for (i = 0; i < m_NDims; i++) { @@ -633,6 +674,10 @@ MetaObject::Origin(const double * _position) void MetaObject::Origin(int _i, double _value) { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Origin is deprecated, please use Offset" << '\n'; + } m_Offset[_i] = _value; } @@ -667,18 +712,30 @@ MetaObject::TransformMatrix(int _i, int _j, double _value) const double * MetaObject::Rotation() const { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Rotation is deprecated, please use TransformMatrix" << '\n'; + } return m_TransformMatrix; } double MetaObject::Rotation(int _i, int _j) const { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Rotation is deprecated, please use TransformMatrix" << '\n'; + } return m_TransformMatrix[_i * m_NDims + _j]; } void MetaObject::Rotation(const double * _orientation) { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Rotation is deprecated, please use TransformMatrix" << '\n'; + } int i; for (i = 0; i < m_NDims * m_NDims; i++) { @@ -689,24 +746,41 @@ MetaObject::Rotation(const double * _orientation) void MetaObject::Rotation(int _i, int _j, double _value) { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Rotation is deprecated, please use TransformMatrix" << '\n'; + } m_TransformMatrix[_i * m_NDims + _j] = _value; } const double * MetaObject::Orientation() const { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Orientation is deprecated, please use TransformMatrix" << '\n'; + } return m_TransformMatrix; } + double MetaObject::Orientation(int _i, int _j) const { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Orientation is deprecated, please use TransformMatrix" << '\n'; + } return m_TransformMatrix[_i * m_NDims + _j]; } void MetaObject::Orientation(const double * _orientation) { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Orientation is deprecated, please use TransformMatrix" << '\n'; + } int i; for (i = 0; i < m_NDims * m_NDims; i++) { @@ -717,6 +791,10 @@ MetaObject::Orientation(const double * _orientation) void MetaObject::Orientation(int _i, int _j, double _value) { + if (m_APIVersion == 1) + { + std::cerr << "MetaIO: Orientation is deprecated, please use TransformMatrix" << '\n'; + } m_TransformMatrix[_i * m_NDims + _j] = _value; } @@ -1041,7 +1119,6 @@ MetaObject::Clear() memset(m_Offset, 0, sizeof(m_Offset)); memset(m_TransformMatrix, 0, sizeof(m_TransformMatrix)); memset(m_CenterOfRotation, 0, sizeof(m_CenterOfRotation)); - memset(m_Color, 0, sizeof(m_Color)); m_ID = -1; m_Color[0] = 1.0F; @@ -1060,24 +1137,15 @@ MetaObject::Clear() m_DistanceUnits = MET_DISTANCE_UNITS_UNKNOWN; META_DEBUG_PRINT( "MetaObject: Clear: m_NDims=" << m_NDims ); - int i; - for (i = 0; i < 10; i++) + if (m_NDims < 10) { - m_ElementSpacing[i] = 1; - m_AnatomicalOrientation[i] = MET_ORIENTATION_UNKNOWN; + for (int i = 0; i < m_NDims; i++) + { + m_ElementSpacing[i] = 1; + m_TransformMatrix[i * m_NDims + i] = 1; + m_AnatomicalOrientation[i] = MET_ORIENTATION_UNKNOWN; + } } - /* - std::vector::iterator fieldIter; - for(fieldIter=m_Fields.begin(); fieldIter!=m_Fields.end(); fieldIter++) - { - if(META_DEBUG) std::cout << "field = " << (*fieldIter)->name << std::endl; - MET_FieldRecordType* field = *fieldIter; - delete field; - field = nullptr; - if(META_DEBUG) std::cout << " has been deleted." << std::endl; - } - m_Fields.clear();*/ - this->ClearFields(); } bool @@ -1085,7 +1153,7 @@ MetaObject::InitializeEssential(int _nDims) { META_DEBUG_PRINT( "MetaObject: Initialize" ); -MetaObject::M_Destroy(); + MetaObject::M_Destroy(); if (_nDims > 10) { @@ -1103,6 +1171,15 @@ MetaObject::M_Destroy(); m_NDims = _nDims; + memset(m_Offset, 0, sizeof(m_Offset)); + memset(m_TransformMatrix, 0, sizeof(m_TransformMatrix)); + for (int i = 0; i < m_NDims; i++) + { + m_ElementSpacing[i] = 1; + m_TransformMatrix[i * m_NDims + i] = 1; + m_AnatomicalOrientation[i] = MET_ORIENTATION_UNKNOWN; + } + return true; } @@ -1115,30 +1192,34 @@ MetaObject::M_Destroy() void MetaObject::M_SetupReadFields() { - this->ClearFields(); META_DEBUG_PRINT( "MetaObject: M_SetupReadFields" ); + this->ClearFields(); + MET_FieldRecordType * mF; mF = new MET_FieldRecordType; - MET_InitReadField(mF, "Comment", MET_STRING, false); + MET_InitReadField(mF, "ObjectType", MET_STRING, false); m_Fields.push_back(mF); mF = new MET_FieldRecordType; - MET_InitReadField(mF, "AcquisitionDate", MET_STRING, false); + MET_InitReadField(mF, "ObjectSubType", MET_STRING, false); m_Fields.push_back(mF); mF = new MET_FieldRecordType; - MET_InitReadField(mF, "ObjectType", MET_STRING, false); + MET_InitReadField(mF, "FileFormatVersion", MET_UINT, false); m_Fields.push_back(mF); mF = new MET_FieldRecordType; - MET_InitReadField(mF, "ObjectSubType", MET_STRING, false); + MET_InitReadField(mF, "Comment", MET_STRING, false); + m_Fields.push_back(mF); + + mF = new MET_FieldRecordType; + MET_InitReadField(mF, "AcquisitionDate", MET_STRING, false); m_Fields.push_back(mF); mF = new MET_FieldRecordType; MET_InitReadField(mF, "NDims", MET_INT, true); - mF->required = true; m_Fields.push_back(mF); int nDimsRecordNumber = MET_GetFieldRecordNumber("NDims", &m_Fields); @@ -1221,7 +1302,6 @@ MetaObject::M_SetupReadFields() mF = new MET_FieldRecordType; MET_InitReadField(mF, "ElementSpacing", MET_FLOAT_ARRAY, false, nDimsRecordNumber); - mF->required = false; m_Fields.push_back(mF); // Add User's field @@ -1234,7 +1314,6 @@ MetaObject::M_SetupReadFields() } } - void MetaObject::M_SetupWriteFields() { @@ -1246,13 +1325,6 @@ MetaObject::M_SetupWriteFields() MET_FieldRecordType * mF; - if (strlen(m_Comment) > 0) - { - mF = new MET_FieldRecordType; - MET_InitWriteField(mF, "Comment", MET_STRING, strlen(m_Comment), m_Comment); - m_Fields.push_back(mF); - } - mF = new MET_FieldRecordType; MET_InitWriteField(mF, "ObjectType", MET_STRING, strlen(m_ObjectTypeName), m_ObjectTypeName); m_Fields.push_back(mF); @@ -1264,6 +1336,20 @@ MetaObject::M_SetupWriteFields() m_Fields.push_back(mF); } + if (m_FileFormatVersion > 0) + { + mF = new MET_FieldRecordType; + MET_InitWriteField(mF, "FileFormatVersion", MET_UINT, m_FileFormatVersion); + m_Fields.push_back(mF); + } + + if (strlen(m_Comment) > 0) + { + mF = new MET_FieldRecordType; + MET_InitWriteField(mF, "Comment", MET_STRING, strlen(m_Comment), m_Comment); + m_Fields.push_back(mF); + } + mF = new MET_FieldRecordType; MET_InitWriteField(mF, "NDims", MET_INT, m_NDims); m_Fields.push_back(mF); @@ -1428,33 +1514,43 @@ MetaObject::M_Read() MET_FieldRecordType * mF; - mF = MET_GetFieldRecord("Comment", &m_Fields); + mF = MET_GetFieldRecord("NDims", &m_Fields); if (mF && mF->defined) { - strcpy(m_Comment, reinterpret_cast(mF->value)); + m_NDims = static_cast(mF->value[0]); + } + if (m_NDims > 0) + { + MetaObject::InitializeEssential(m_NDims); + Clear(); } - mF = MET_GetFieldRecord("ObjectType", &m_Fields); + mF = MET_GetFieldRecord("FileFormatVersion", &m_Fields); if (mF && mF->defined) { - strcpy(m_ObjectTypeName, reinterpret_cast(mF->value)); + m_FileFormatVersion = static_cast(mF->value[0]); + } + else + { + m_FileFormatVersion = 0; } - mF = MET_GetFieldRecord("ObjectSubType", &m_Fields); + mF = MET_GetFieldRecord("Comment", &m_Fields); if (mF && mF->defined) { - strcpy(m_ObjectSubTypeName, reinterpret_cast(mF->value)); + strcpy(m_Comment, reinterpret_cast(mF->value)); } - mF = MET_GetFieldRecord("NDims", &m_Fields); + mF = MET_GetFieldRecord("ObjectType", &m_Fields); if (mF && mF->defined) { - m_NDims = static_cast(mF->value[0]); + strcpy(m_ObjectTypeName, reinterpret_cast(mF->value)); } - if (m_NDims > 0) + mF = MET_GetFieldRecord("ObjectSubType", &m_Fields); + if (mF && mF->defined) { - MetaObject::InitializeEssential(m_NDims); + strcpy(m_ObjectSubTypeName, reinterpret_cast(mF->value)); } mF = MET_GetFieldRecord("Name", &m_Fields); @@ -1545,87 +1641,70 @@ MetaObject::M_Read() int i; mF = MET_GetFieldRecord("Color", &m_Fields); - if (mF) + if (mF && mF->defined) { - if (mF->defined) + for (i = 0; i < mF->length && i < 4; i++) { - for (i = 0; i < mF->length && i < 4; i++) - { - m_Color[i] = static_cast(mF->value[i]); - } - } - else - { - for (i = 0; i < mF->length && i < 4; i++) - { - m_Color[i] = static_cast(1); - } + m_Color[i] = static_cast(mF->value[i]); } } - mF = MET_GetFieldRecord("Position", &m_Fields); + mF = MET_GetFieldRecord("Offset", &m_Fields); if (mF && mF->defined) { - for (i = 0; i < mF->length; i++) + for (i = 0; i < mF->length && i < m_NDims; i++) { m_Offset[i] = mF->value[i]; } } - mF = MET_GetFieldRecord("Offset", &m_Fields); - if (mF && mF->defined) + + if (m_FileFormatVersion == 0) { - for (i = 0; i < mF->length; i++) + mF = MET_GetFieldRecord("Position", &m_Fields); + if (mF && mF->defined) { - m_Offset[i] = mF->value[i]; + for (i = 0; i < mF->length && i < m_NDims; i++) + { + m_Offset[i] = mF->value[i]; + } } - } - mF = MET_GetFieldRecord("Origin", &m_Fields); - if (mF && mF->defined) - { - for (i = 0; i < mF->length; i++) + mF = MET_GetFieldRecord("Origin", &m_Fields); + if (mF && mF->defined) { - m_Offset[i] = mF->value[i]; + for (i = 0; i < mF->length && i < m_NDims; i++) + { + m_Offset[i] = mF->value[i]; + } } - } - - bool transformMatrixDefined = false; - mF = MET_GetFieldRecord("Orientation", &m_Fields); - if (mF && mF->defined) - { - transformMatrixDefined = true; - int len = mF->length; - for (i = 0; i < len * len; i++) + mF = MET_GetFieldRecord("Orientation", &m_Fields); + if (mF && mF->defined) { - m_TransformMatrix[i] = mF->value[i]; + int len = mF->length; + for (i = 0; i < len*len && i < m_NDims*m_NDims; i++) + { + m_TransformMatrix[i] = mF->value[i]; + } } - } - mF = MET_GetFieldRecord("Rotation", &m_Fields); - if (mF && mF->defined) - { - transformMatrixDefined = true; - int len = mF->length; - for (i = 0; i < len * len; i++) + mF = MET_GetFieldRecord("Rotation", &m_Fields); + if (mF && mF->defined) { - m_TransformMatrix[i] = mF->value[i]; + int len = mF->length; + for (i = 0; i < len*len && i < m_NDims*m_NDims; i++) + { + m_TransformMatrix[i] = mF->value[i]; + } } } + mF = MET_GetFieldRecord("TransformMatrix", &m_Fields); if (mF && mF->defined) { - transformMatrixDefined = true; int len = mF->length; - for (i = 0; i < len * len; i++) + for (i = 0; i < len*len && i < m_NDims*m_NDims; i++) { m_TransformMatrix[i] = mF->value[i]; } } - if (!transformMatrixDefined) - { - for (i = 0; i < m_NDims; i++) - { - m_TransformMatrix[i + i * m_NDims] = 1; - } - } mF = MET_GetFieldRecord("CenterOfRotation", &m_Fields); if (mF && mF->defined) @@ -1635,13 +1714,6 @@ MetaObject::M_Read() m_CenterOfRotation[i] = mF->value[i]; } } - else - { - for (i = 0; i < m_NDims; i++) - { - m_CenterOfRotation[i] = 0; - } - } mF = MET_GetFieldRecord("DistanceUnits", &m_Fields); if (mF && mF->defined) @@ -1656,44 +1728,39 @@ MetaObject::M_Read() } mF = MET_GetFieldRecord("ElementSpacing", &m_Fields); - if (mF) + if (mF && mF->defined) { - if (mF->defined) + for (i = 0; i < mF->length && i < 10; i++) { - for (i = 0; i < mF->length && i < 10; i++) - { - m_ElementSpacing[i] = mF->value[i]; - META_DEBUG_PRINT( "metaObject: M_Read: elementSpacing[" << i << "] = " << m_ElementSpacing[i] ); - } - } - else - { - for (i = 0; i < mF->length && i < 10; i++) - { - m_ElementSpacing[i] = 1; - META_DEBUG_PRINT( "metaObject: M_Read: elementSpacing[" << i << "] = " << m_ElementSpacing[i] ); - } + m_ElementSpacing[i] = mF->value[i]; + META_DEBUG_PRINT( "metaObject: M_Read: elementSpacing[" << i << "] = " << m_ElementSpacing[i] ); } } - // Set the read record field in the m_UserDefinedWriteFields + // If a field is read, add it to the list of fields to be written auto it = m_UserDefinedReadFields.begin(); auto end = m_UserDefinedReadFields.end(); while (it != end) { mF = MET_GetFieldRecord((*it)->name, &m_Fields); - // DON'T put the same cross-linked element from the UD readFields - // into the userDefined write fields more than once. That - // causes a double free, and an abort. + if( !mF ) + { + ++it; + continue; + } + + // Don't add a read field to the write fields if it is already in the write fields + bool found = false; FieldsContainerType::iterator dup; for (dup = m_UserDefinedWriteFields.begin(); dup != m_UserDefinedWriteFields.end(); ++dup) { if ((*dup) == mF) { + found = true; break; } } - if (dup == m_UserDefinedWriteFields.end()) + if (!found) { m_UserDefinedWriteFields.push_back(mF); } @@ -1732,27 +1799,16 @@ MetaObject ::Append(const char * _headName) if (!m_WriteStream) { - m_WriteStream = new std::ofstream; + m_WriteStream = new METAIO_STREAM::ofstream; } -#ifdef __sgi - m_WriteStream->open(m_FileName, std::ios::out | std::ios::in); - if (!m_WriteStream->rdbuf()->is_open()) - { - delete m_WriteStream; - m_WriteStream = 0; - return false; - } - m_WriteStream->seekp(0, std::ios::end); -#else - m_WriteStream->open(m_FileName, std::ios::binary | std::ios::out | std::ios::app); + m_WriteStream->open(m_FileName.c_str(), std::ios::binary | std::ios::out | std::ios::app); if (!m_WriteStream->rdbuf()->is_open()) { delete m_WriteStream; m_WriteStream = nullptr; return false; } -#endif M_Write(); @@ -1768,10 +1824,15 @@ MetaObject ::Append(const char * _headName) void * MetaObject ::GetUserField(const char * _name) { - auto it = m_UserDefinedWriteFields.begin(); - auto end = m_UserDefinedWriteFields.end(); + auto it = m_UserDefinedReadFields.begin(); + auto end = m_UserDefinedReadFields.end(); while (it != end) { + if (*it == nullptr) + { + ++it; + continue; + } int eSize; MET_SizeOfType((*it)->type, &eSize); const auto itLength = static_cast((*it)->length); @@ -1837,6 +1898,16 @@ MetaObject ::GetAdditionalReadFieldValueLength(int i) bool MetaObject ::AddUserField(const char * _fieldName, MET_ValueEnumType _type, int _length, bool _required, int _dependsOn) { + auto it = m_UserDefinedReadFields.begin(); + auto end = m_UserDefinedReadFields.end(); + while (it != end) + { + if (!strcmp((*it)->name, _fieldName)) + { + return false; + } + ++it; + } auto * mFr = new MET_FieldRecordType; MET_InitReadField(mFr, _fieldName, _type, _required, _dependsOn, static_cast(_length)); m_UserDefinedReadFields.push_back(mFr); @@ -1856,7 +1927,7 @@ MetaObject::M_PrepareNewReadStream() } else { - m_ReadStream = new std::ifstream; + m_ReadStream = new METAIO_STREAM::ifstream; } } diff --git a/src/metaObject.h b/src/metaObject.h index 128bd69f297..5d6a9f9d910 100644 --- a/src/metaObject.h +++ b/src/metaObject.h @@ -35,6 +35,7 @@ class METAIO_EXPORT MetaObject // PROTECTED protected: std::set m_ReservedKeywords = { + "FileFormatVersion", "ObjectType", "ObjectSubType", "NDims", @@ -56,14 +57,17 @@ class METAIO_EXPORT MetaObject typedef std::vector FieldsContainerType; - std::ifstream * m_ReadStream; - std::ofstream * m_WriteStream; + METAIO_STREAM::ifstream * m_ReadStream; + METAIO_STREAM::ofstream * m_WriteStream; FieldsContainerType m_Fields; FieldsContainerType m_UserDefinedWriteFields; FieldsContainerType m_UserDefinedReadFields; FieldsContainerType m_AdditionalReadFields; + unsigned int m_FileFormatVersion; + unsigned int m_APIVersion; + std::string m_FileName; char m_Comment[255]{}; // "Comment = " "" @@ -142,6 +146,16 @@ class METAIO_EXPORT MetaObject std::set GetReservedKeywords() const; + void + FileFormatVersion(unsigned int _fileFormatVersion); + unsigned int + FileFormatVersion() const; + + void + APIVersion(unsigned int _APIVersion); + unsigned int + APIVersion() const; + void FileName(const char * _fileName); const char * @@ -154,7 +168,7 @@ class METAIO_EXPORT MetaObject Read(const char * _fileName = nullptr); bool - ReadStream(int _nDims, std::ifstream * _stream); + ReadStream(int _nDims, METAIO_STREAM::ifstream * _stream); virtual bool Write(const char * _fileName = nullptr); @@ -204,21 +218,21 @@ class METAIO_EXPORT MetaObject Offset(const double * _position); void Offset(int _i, double _value); - const double * + virtual const double * Position() const; - double + virtual double Position(int _i) const; - void + virtual void Position(const double * _position); - void + virtual void Position(int _i, double _value); - const double * + virtual const double * Origin() const; - double + virtual double Origin(int _i) const; - void + virtual void Origin(const double * _position); - void + virtual void Origin(int _i, double _value); // TransformMatrix(...) diff --git a/src/metaScene.cxx b/src/metaScene.cxx index 2ca9be4cdf8..1edb52bacd6 100644 --- a/src/metaScene.cxx +++ b/src/metaScene.cxx @@ -115,7 +115,7 @@ MetaScene::Read(const char * _headerName) strcpy(suf, &_headerName[i]); } -MetaObject::M_Destroy(); + MetaObject::M_Destroy(); Clear(); @@ -130,7 +130,7 @@ MetaObject::M_Destroy(); M_PrepareNewReadStream(); - m_ReadStream->open(m_FileName, std::ios::binary | std::ios::in); + m_ReadStream->open(m_FileName.c_str(), std::ios::binary | std::ios::in); if (!m_ReadStream->rdbuf()->is_open()) { @@ -166,19 +166,21 @@ MetaObject::M_Destroy(); } const std::string objectType = MET_ReadType(*m_ReadStream); - if (!strncmp(objectType.c_str(), "Tube", 4) || ((objectType.empty()) && !strcmp(suf, "tre"))) + if (objectType.find("Tube") != std::string::npos || ((objectType.empty()) && !strcmp(suf, "tre"))) { - char * subtype = MET_ReadSubType(*m_ReadStream); - if (!strncmp(subtype, "Vessel", 6)) + const std::string subtype = MET_ReadSubType(*m_ReadStream); + if (subtype.find("Vessel") != std::string::npos) { auto * vesseltube = new MetaVesselTube(); + vesseltube->APIVersion(m_APIVersion); vesseltube->SetEvent(m_Event); vesseltube->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(vesseltube); } - else if (!strncmp(subtype, "DTI", 3)) + else if (subtype.find("DTI") != std::string::npos) { auto * dtitube = new MetaDTITube(); + dtitube->APIVersion(m_APIVersion); dtitube->SetEvent(m_Event); dtitube->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(dtitube); @@ -186,128 +188,143 @@ MetaObject::M_Destroy(); else { auto * tube = new MetaTube(); + tube->APIVersion(m_APIVersion); tube->SetEvent(m_Event); tube->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(tube); } - delete[] subtype; } - else if (!strncmp(objectType.c_str(), "Transform", 9)) + else if (objectType.find("Transform") != std::string::npos) { auto * transform = new MetaTransform(); + transform->APIVersion(m_APIVersion); transform->SetEvent(m_Event); transform->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(transform); } - else if (!strncmp(objectType.c_str(), "TubeGraph", 9)) + else if (objectType.find("TubeGraph") != std::string::npos) { auto * tubeGraph = new MetaTubeGraph(); + tubeGraph->APIVersion(m_APIVersion); tubeGraph->SetEvent(m_Event); tubeGraph->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(tubeGraph); } - else if (!strncmp(objectType.c_str(), "Ellipse", 7) || ((objectType.empty()) && !strcmp(suf, "elp"))) + else if (objectType.find("Ellipse") != std::string::npos || ((objectType.empty()) && !strcmp(suf, "elp"))) { auto * ellipse = new MetaEllipse(); + ellipse->APIVersion(m_APIVersion); ellipse->SetEvent(m_Event); ellipse->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(ellipse); } - else if (!strncmp(objectType.c_str(), "Contour", 7) || ((objectType.empty()) && !strcmp(suf, "ctr"))) + else if (objectType.find("Contour") != std::string::npos || ((objectType.empty()) && !strcmp(suf, "ctr"))) { auto * contour = new MetaContour(); + contour->APIVersion(m_APIVersion); contour->SetEvent(m_Event); contour->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(contour); } - else if (!strncmp(objectType.c_str(), "Arrow", 5)) + else if (objectType.find("Arrow") != std::string::npos) { auto * arrow = new MetaArrow(); + arrow->APIVersion(m_APIVersion); arrow->SetEvent(m_Event); arrow->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(arrow); } - else if (!strncmp(objectType.c_str(), "Gaussian", 8) || ((objectType.empty()) && !strcmp(suf, "gau"))) + else if (objectType.find("Gaussian") != std::string::npos || ((objectType.empty()) && !strcmp(suf, "gau"))) { auto * gaussian = new MetaGaussian(); + gaussian->APIVersion(m_APIVersion); gaussian->SetEvent(m_Event); gaussian->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(gaussian); } - else if (!strncmp(objectType.c_str(), "Image", 5) || + else if (objectType.find("Image") != std::string::npos || ((objectType.empty()) && (!strcmp(suf, "mhd") || !strcmp(suf, "mha")))) { auto * image = new MetaImage(); + image->APIVersion(m_APIVersion); image->SetEvent(m_Event); image->ReadStream(m_NDims, m_ReadStream); image->ElementByteOrderFix(); m_ObjectList.push_back(image); } - else if (!strncmp(objectType.c_str(), "Blob", 4) || ((objectType.empty()) && !strcmp(suf, "blb"))) + else if (objectType.find("Blob") != std::string::npos || ((objectType.empty()) && !strcmp(suf, "blb"))) { auto * blob = new MetaBlob(); + blob->APIVersion(m_APIVersion); blob->SetEvent(m_Event); blob->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(blob); } - else if (!strncmp(objectType.c_str(), "Landmark", 8) || ((objectType.empty()) && !strcmp(suf, "ldm"))) + else if (objectType.find("Landmark") != std::string::npos || ((objectType.empty()) && !strcmp(suf, "ldm"))) { auto * landmark = new MetaLandmark(); + landmark->APIVersion(m_APIVersion); landmark->SetEvent(m_Event); landmark->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(landmark); } - else if (!strncmp(objectType.c_str(), "Surface", 5) || ((objectType.empty()) && !strcmp(suf, "suf"))) + else if (objectType.find("Surface") != std::string::npos || ((objectType.empty()) && !strcmp(suf, "suf"))) { auto * surface = new MetaSurface(); + surface->APIVersion(m_APIVersion); surface->SetEvent(m_Event); surface->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(surface); } - else if (!strncmp(objectType.c_str(), "Line", 4) || ((objectType.empty()) && !strcmp(suf, "lin"))) + else if (objectType.find("Line") != std::string::npos || ((objectType.empty()) && !strcmp(suf, "lin"))) { auto * line = new MetaLine(); + line->APIVersion(m_APIVersion); line->SetEvent(m_Event); line->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(line); } - else if (!strncmp(objectType.c_str(), "Group", 5) || ((objectType.empty()) && !strcmp(suf, "grp"))) + else if (objectType.find("Group") != std::string::npos || ((objectType.empty()) && !strcmp(suf, "grp"))) { auto * group = new MetaGroup(); + group->APIVersion(m_APIVersion); group->SetEvent(m_Event); group->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(group); } - else if (!strncmp(objectType.c_str(), "AffineTransform", 15) || ((objectType.empty()) && !strcmp(suf, "trn"))) + else if (objectType.find("AffineTransform") != std::string::npos || ((objectType.empty()) && !strcmp(suf, "trn"))) { auto * group = new MetaGroup(); + group->APIVersion(m_APIVersion); group->SetEvent(m_Event); group->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(group); } - else if (!strncmp(objectType.c_str(), "Mesh", 4) || ((objectType.empty()) && !strcmp(suf, "msh"))) + else if (objectType.find("Mesh") != std::string::npos || ((objectType.empty()) && !strcmp(suf, "msh"))) { auto * mesh = new MetaMesh(); + mesh->APIVersion(m_APIVersion); mesh->SetEvent(m_Event); mesh->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(mesh); } - else if (!strncmp(objectType.c_str(), "FEMObject", 9) || ((objectType.empty()) && !strcmp(suf, "fem"))) + else if (objectType.find("FEMObject") != std::string::npos || ((objectType.empty()) && !strcmp(suf, "fem"))) { auto * femobject = new MetaFEMObject(); + femobject->APIVersion(m_APIVersion); femobject->SetEvent(m_Event); femobject->ReadStream(m_NDims, m_ReadStream); m_ObjectList.push_back(femobject); @@ -343,19 +360,10 @@ MetaScene::Write(const char * _headName) if (!m_WriteStream) { - m_WriteStream = new std::ofstream; + m_WriteStream = new METAIO_STREAM::ofstream; } -#ifdef __sgi - // Create the file. This is required on some older sgi's - { - std::ofstream tFile(m_FileName, std::ios::out); - tFile.close(); - } - m_WriteStream->open(m_FileName, std::ios::out); -#else - m_WriteStream->open(m_FileName, std::ios::binary | std::ios::out); -#endif + m_WriteStream->open(m_FileName.c_str(), std::ios::binary | std::ios::out); if (!m_WriteStream->rdbuf()->is_open()) { @@ -414,8 +422,7 @@ MetaScene::M_SetupReadFields() MET_FieldRecordType * mF; mF = new MET_FieldRecordType; - MET_InitReadField(mF, "NObjects", MET_INT, false); - mF->required = true; + MET_InitReadField(mF, "NObjects", MET_INT, true); mF->terminateRead = true; m_Fields.push_back(mF); @@ -430,6 +437,17 @@ MetaScene::M_SetupWriteFields() MET_FieldRecordType * mF; + mF = new MET_FieldRecordType; + MET_InitWriteField(mF, "ObjectType", MET_STRING, strlen(m_ObjectTypeName), m_ObjectTypeName); + m_Fields.push_back(mF); + + if (m_FileFormatVersion > 0) + { + mF = new MET_FieldRecordType; + MET_InitWriteField(mF, "FileFormatVersion", MET_UINT, m_FileFormatVersion); + m_Fields.push_back(mF); + } + if (strlen(m_Comment) > 0) { mF = new MET_FieldRecordType; @@ -437,10 +455,6 @@ MetaScene::M_SetupWriteFields() m_Fields.push_back(mF); } - mF = new MET_FieldRecordType; - MET_InitWriteField(mF, "ObjectType", MET_STRING, strlen(m_ObjectTypeName), m_ObjectTypeName); - m_Fields.push_back(mF); - mF = new MET_FieldRecordType; MET_InitWriteField(mF, "NDims", MET_INT, m_NDims); m_Fields.push_back(mF); @@ -473,7 +487,7 @@ MetaScene::M_Read() MET_FieldRecordType * mF; mF = MET_GetFieldRecord("NObjects", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { m_NObjects = static_cast(mF->value[0]); } diff --git a/src/metaSurface.cxx b/src/metaSurface.cxx index 1883f9f09af..151c4f5eb6f 100644 --- a/src/metaSurface.cxx +++ b/src/metaSurface.cxx @@ -244,19 +244,19 @@ MetaSurface::M_Read() MET_FieldRecordType * mF; mF = MET_GetFieldRecord("NPoints", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { m_NPoints = static_cast(mF->value[0]); } mF = MET_GetFieldRecord("ElementType", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { MET_StringToType(reinterpret_cast(mF->value), &m_ElementType); } mF = MET_GetFieldRecord("PointDim", &m_Fields); - if (mF->defined) + if (mF && mF->defined) { strcpy(m_PointDim, reinterpret_cast(mF->value)); } diff --git a/src/metaSurface.h b/src/metaSurface.h index ac67f36629f..52b47b92223 100644 --- a/src/metaSurface.h +++ b/src/metaSurface.h @@ -40,7 +40,7 @@ namespace METAIO_NAMESPACE { # endif -class SurfacePnt +class METAIO_EXPORT SurfacePnt { public: explicit SurfacePnt(int dim); diff --git a/src/metaUtils.cxx b/src/metaUtils.cxx index 591fd663a70..e37635bf36a 100644 --- a/src/metaUtils.cxx +++ b/src/metaUtils.cxx @@ -57,7 +57,7 @@ bool META_DEBUG = false; static char MET_SeperatorChar = '='; -static const std::streamoff MET_MaxChunkSize = 1024 * 1024 * 1024; +constexpr static std::streamoff MET_MaxChunkSize = 1024 * 1024 * 1024; MET_FieldRecordType * MET_GetFieldRecord(const char * _fieldName, std::vector * _fields) @@ -110,7 +110,7 @@ MET_SizeOfType(MET_ValueEnumType _vType, int * s) bool MET_SystemByteOrderMSB() { - const int l = 1; + constexpr int l = 1; const char * u = reinterpret_cast(&l); if (u[0]) @@ -162,11 +162,10 @@ MET_ReadType(std::istream & _fp) std::vector fields; auto * mF = new MET_FieldRecordType; MET_InitReadField(mF, "ObjectType", MET_STRING, false); - mF->required = false; mF->terminateRead = true; fields.push_back(mF); - MET_Read(_fp, &fields, '=', true); + MET_Read(_fp, &fields, '=', true, false); _fp.seekg(pos); if (mF->defined) @@ -183,35 +182,29 @@ MET_ReadType(std::istream & _fp) // // Read the subtype of the object // -char * +std::string MET_ReadSubType(std::istream & _fp) { std::streampos pos = _fp.tellg(); std::vector fields; MET_FieldRecordType * mF; mF = new MET_FieldRecordType; - MET_InitReadField(mF, "ObjectType", MET_STRING, false); - mF->required = false; + MET_InitReadField(mF, "ObjectSubType", MET_STRING, false); + mF->terminateRead = true; fields.push_back(mF); - MET_Read(_fp, &fields, '=', true); + MET_Read(_fp, &fields, '=', true, false); + _fp.seekg(pos); - // Find the line right after the ObjectType - char s[1024]; - _fp.getline(s, 500); - std::string value = s; - size_t position = value.find('='); - if (position != std::string::npos) + if (mF->defined) { - value = value.substr(position + 2, value.size() - position); + std::string value = reinterpret_cast(mF->value); + delete mF; + return value; } - _fp.seekg(pos); - char * ret = new char[value.size() + 1]; - strncpy(ret, value.c_str(), value.size()); - ret[value.size()] = '\0'; delete mF; - return ret; + return std::string(); } @@ -590,7 +583,7 @@ MET_ValueToValue(MET_ValueEnumType _fromType, // Uncompress a stream given an uncompressedSeekPosition METAIO_EXPORT std::streamoff -MET_UncompressStream(std::ifstream * stream, +MET_UncompressStream(METAIO_STREAM::ifstream * stream, std::streamoff uncompressedSeekPosition, unsigned char * uncompressedData, std::streamoff uncompressedDataSize, @@ -883,6 +876,10 @@ MET_PerformUncompression(const unsigned char * sourceCompressed, d_stream.next_out = uncompressedData + dest_pos; d_stream.avail_out = cur_remain_chunk; err = inflate(&d_stream, Z_NO_FLUSH); + // Account for this call's output before any exit, including the final + // call that reports Z_STREAM_END. + uInt count_uncompressed = cur_remain_chunk - d_stream.avail_out; + dest_pos += count_uncompressed; if (err == Z_STREAM_END || err < 0) { if (err != Z_STREAM_END && err != Z_BUF_ERROR) // Z_BUF_ERROR means there is still data to uncompress, @@ -891,11 +888,21 @@ MET_PerformUncompression(const unsigned char * sourceCompressed, } break; } - uInt count_uncompressed = cur_remain_chunk - d_stream.avail_out; - dest_pos += count_uncompressed; } while (d_stream.avail_out == 0); } while (err != Z_STREAM_END && err >= 0); inflateEnd(&d_stream); + if (err != Z_STREAM_END) + { + std::cerr << "MET_PerformUncompression: compressed stream did not terminate cleanly (zlib error " << err << ")" + << '\n'; + return false; + } + if (dest_pos != uncompressedDataSize) + { + std::cerr << "MET_PerformUncompression: expected " << uncompressedDataSize << " bytes, produced " << dest_pos + << '\n'; + return false; + } return true; } @@ -1165,6 +1172,7 @@ MET_Read(std::istream & fp, MET_SeperatorChar = _met_SeperatorChar; + unsigned int linecount = 0; while (!fp.eof()) { int i = 0; @@ -1426,7 +1434,7 @@ MET_Read(std::istream & fp, fp.getline(s, 500); } } - if (oneLine) + if (oneLine && ++linecount > 3) { return MET_IsComplete(fields); } @@ -1910,6 +1918,66 @@ MET_SwapByteIfSystemMSB(void * val, MET_ValueEnumType _type) } } + +void MET_PrintFieldRecord(std::ostream & _fp, MET_FieldRecordType * _mf) +{ + if (_mf == nullptr) + { + _fp << "NULL Field Record" << '\n'; + return; + } + _fp << "Name = " << _mf->name << '\n'; + _fp << "Type = " << MET_ValueTypeName[_mf->type] << '\n'; + _fp << "Defined = " << _mf->defined << '\n'; + _fp << "Length = " << _mf->length << '\n'; + _fp << "DependsOn = " << _mf->dependsOn << '\n'; + _fp << "Required = " << _mf->required << '\n'; + _fp << "TerminateRead = " << _mf->terminateRead << '\n'; + _fp << "Value = "; + if (!_mf->defined) + { + _fp << "Undefined" << '\n'; + return; + } + if (_mf->type == MET_STRING) + { + _fp << reinterpret_cast(_mf->value) << '\n'; + } + else if (_mf->type == MET_ASCII_CHAR || _mf->type == MET_CHAR || _mf->type == MET_UCHAR || + _mf->type == MET_SHORT || _mf->type == MET_USHORT || _mf->type == MET_LONG || + _mf->type == MET_ULONG || _mf->type == MET_INT || _mf->type == MET_UINT || + _mf->type == MET_FLOAT || _mf->type == MET_DOUBLE) + { + _fp << _mf->name << " : " << _mf->value[0] << '\n'; + } + else if (_mf->type == MET_CHAR_ARRAY || _mf->type == MET_UCHAR_ARRAY || _mf->type == MET_SHORT_ARRAY || + _mf->type == MET_USHORT_ARRAY || _mf->type == MET_INT_ARRAY || _mf->type == MET_UINT_ARRAY || + _mf->type == MET_FLOAT_ARRAY || _mf->type == MET_DOUBLE_ARRAY) + { + _fp << _mf->value[0]; + for (int i = 1; i < _mf->length; i++) + { + _fp << ", " << _mf->value[i]; + } + _fp << '\n'; + } + else if (_mf->type == MET_FLOAT_MATRIX) + { + _fp << '\n'; + int count = 0; + for (int i = 0; i < _mf->length; i++) + { + _fp << _mf->value[count++]; + for (int j = 1; j < _mf->length; j++) + { + _fp << ", " << _mf->value[count++]; + } + _fp << '\n'; + } + } +} + + #if (METAIO_USE_NAMESPACE) }; #endif diff --git a/src/metaUtils.h b/src/metaUtils.h index 0deef82a311..2d2ad50ef20 100644 --- a/src/metaUtils.h +++ b/src/metaUtils.h @@ -349,7 +349,7 @@ MET_PerformUncompression(const unsigned char * sourceCompressed, // Uncompress a stream given an uncompressedSeekPosition METAIO_EXPORT std::streamoff -MET_UncompressStream(std::ifstream * stream, +MET_UncompressStream(METAIO_STREAM::ifstream * stream, std::streamoff uncompressedSeekPosition, unsigned char * uncompressedData, std::streamoff uncompressedDataSize, @@ -442,6 +442,10 @@ MET_Read(std::istream & fp, std::vector * newFields = nullptr); +METAIO_EXPORT +void +MET_PrintFieldRecord(std::ostream & _fp, MET_FieldRecordType * _mf); + METAIO_EXPORT std::string MET_ReadForm(std::istream & _fp); @@ -451,7 +455,7 @@ std::string MET_ReadType(std::istream & _fp); METAIO_EXPORT -char * +std::string MET_ReadSubType(std::istream & _fp); # if (METAIO_USE_NAMESPACE) diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 2f187e2dd09..c0f470d1124 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -8,7 +8,7 @@ endif() function(MetaAddTest ExeName) add_executable( ${ExeName} ${ExeName}.cxx ) - target_link_libraries( ${ExeName} ${METAIO_NAMESPACE} ${ZLIB_LIBRARIES}) + target_link_libraries( ${ExeName} ${METAIO_NAMESPACE} ${METAIO_ZLIB_LIBRARIES}) add_test(NAME ${ExeName} COMMAND ${ExeName} WORKING_DIRECTORY ${ITK_TEST_OUTPUT_DIR}) @@ -26,3 +26,5 @@ MetaAddTest(testMeta9Landmark) MetaAddTest(testMeta10Contour) MetaAddTest(testMeta11Form) MetaAddTest(testMeta12Array) +MetaAddTest(testMeta13ImageList) +MetaAddTest(testMeta14ImageCompressed) diff --git a/src/tests/testMeta13ImageList.cxx b/src/tests/testMeta13ImageList.cxx new file mode 100644 index 00000000000..5d73fff75c7 --- /dev/null +++ b/src/tests/testMeta13ImageList.cxx @@ -0,0 +1,119 @@ +// Regression tests for the "ElementDataFile = LIST" reader. + +#include +#include +#include +#include + +#include + +namespace +{ + +const char * const slice0 = "testMeta13_s0.raw"; +const char * const slice1 = "testMeta13_s1.raw"; + +void +WriteSlice(const char * name, unsigned char value) +{ + std::ofstream out(name, std::ios::binary); + const unsigned char buf[4] = { value, value, value, value }; + out.write(reinterpret_cast(buf), 4); +} + +// A 2x2x2 MET_UCHAR volume whose slices come from a LIST. "listBody" supplies +// the ElementDataFile value and the file names that follow it. +void +WriteHeader(const std::string & name, const std::string & listBody) +{ + std::ofstream out(name.c_str()); + out << "ObjectType = Image\n" + << "NDims = 3\n" + << "DimSize = 2 2 2\n" + << "ElementType = MET_UCHAR\n" + << "ElementSpacing = 1 1 1\n" + << "ElementByteOrderMSB = False\n" + << listBody; +} + +bool +SlicesAreOneThenTwo(MetaImage & image) +{ + for (int i = 0; i < 8; ++i) + { + const int expected = (i < 4) ? 1 : 2; + if (static_cast(image.ElementData(i)) != expected) + { + std::cout << " element " << i << " is " << image.ElementData(i) << ", expected " << expected << '\n'; + return false; + } + } + return true; +} + +} // namespace + +int +main(int, char *[]) +{ + WriteSlice(slice0, 1); + WriteSlice(slice1, 2); + + const std::string bothSlices = std::string(slice0) + "\n" + slice1 + "\n"; + + // A well-formed list reads both slices. + WriteHeader("testMeta13_valid.mhd", "ElementDataFile = LIST\n" + bothSlices); + { + MetaImage image; + if (!image.Read("testMeta13_valid.mhd")) + { + std::cout << "Well-formed LIST failed to read: FAIL" << '\n'; + return EXIT_FAILURE; + } + if (!SlicesAreOneThenTwo(image)) + { + std::cout << "Well-formed LIST read wrong values: FAIL" << '\n'; + return EXIT_FAILURE; + } + } + + // A list naming fewer files than DimSize requires must not report success, + // because the tail of the buffer is never written. + WriteHeader("testMeta13_short.mhd", std::string("ElementDataFile = LIST\n") + slice0 + "\n"); + { + MetaImage image; + if (image.Read("testMeta13_short.mhd")) + { + std::cout << "Short LIST reported success: FAIL" << '\n'; + return EXIT_FAILURE; + } + } + + // An out-of-range file dimension must fall back to NDims - 1 rather than + // indexing m_DimSize/m_SubQuantity outside [0, NDims). + const char * const outOfRange[] = { "ElementDataFile = LIST -1\n", "ElementDataFile = LIST 3\n" }; + for (const char * const listType : outOfRange) + { + WriteHeader("testMeta13_range.mhd", listType + bothSlices); + MetaImage image; + if (!image.Read("testMeta13_range.mhd")) + { + std::cout << "Out-of-range file dimension failed to read: FAIL (" << listType << ')' << '\n'; + return EXIT_FAILURE; + } + if (!SlicesAreOneThenTwo(image)) + { + std::cout << "Out-of-range file dimension read wrong values: FAIL (" << listType << ')' << '\n'; + return EXIT_FAILURE; + } + } + + std::remove(slice0); + std::remove(slice1); + std::remove("testMeta13_valid.mhd"); + std::remove("testMeta13_short.mhd"); + std::remove("testMeta13_range.mhd"); + + std::cout << "LIST element data file tests: PASS" << '\n'; + return EXIT_SUCCESS; +} diff --git a/src/tests/testMeta14ImageCompressed.cxx b/src/tests/testMeta14ImageCompressed.cxx new file mode 100644 index 00000000000..abdfcaca099 --- /dev/null +++ b/src/tests/testMeta14ImageCompressed.cxx @@ -0,0 +1,110 @@ +// Regression tests for reading compressed element data. + +#include +#include +#include +#include +#include + +#include + +namespace +{ + +const char * const localName = "testMeta14_local.mha"; + +// Write a compressed single-file (LOCAL) image, then strip CompressedDataSize +// from its header so the reader has to determine the size itself. +bool +WriteLocalWithoutCompressedDataSize(int quantity) +{ + { + std::vector values(static_cast(quantity)); + for (int i = 0; i < quantity; ++i) + { + values[static_cast(i)] = static_cast(i % 251); + } + MetaImage image(quantity, 1, 1, 1, MET_UCHAR, 1, values.data()); + image.CompressedData(true); + if (!image.Write(localName)) + { + return false; + } + } + + std::ifstream in(localName, std::ios::binary); + std::string contents((std::istreambuf_iterator(in)), std::istreambuf_iterator()); + in.close(); + + const std::string key = "CompressedDataSize = "; + const size_t at = contents.find(key); + if (at == std::string::npos) + { + std::cout << " no CompressedDataSize field was written" << '\n'; + return false; + } + const size_t eol = contents.find('\n', at); + contents.erase(at, eol - at + 1); + + std::ofstream out(localName, std::ios::binary); + out.write(contents.data(), static_cast(contents.size())); + return true; +} + +} // namespace + +int +main(int, char *[]) +{ + const int quantity = 4096; + + // A LOCAL image whose header omits CompressedDataSize: the compressed data + // starts after the header, not at the start of the file. + if (!WriteLocalWithoutCompressedDataSize(quantity)) + { + std::cout << "Could not prepare the LOCAL test image: FAIL" << '\n'; + return EXIT_FAILURE; + } + { + MetaImage image; + if (!image.Read(localName)) + { + std::cout << "LOCAL image without CompressedDataSize failed to read: FAIL" << '\n'; + return EXIT_FAILURE; + } + for (int i = 0; i < quantity; ++i) + { + const int expected = i % 251; + if (static_cast(image.ElementData(i)) != expected) + { + std::cout << " element " << i << " is " << image.ElementData(i) << ", expected " << expected << '\n'; + std::cout << "LOCAL image without CompressedDataSize read wrong values: FAIL" << '\n'; + return EXIT_FAILURE; + } + } + } + + // Truncating the compressed stream must be reported, not silently accepted. + { + std::ifstream in(localName, std::ios::binary); + std::string contents((std::istreambuf_iterator(in)), std::istreambuf_iterator()); + in.close(); + contents.resize(contents.size() - 16); + std::ofstream out("testMeta14_truncated.mha", std::ios::binary); + out.write(contents.data(), static_cast(contents.size())); + out.close(); + + MetaImage image; + if (image.Read("testMeta14_truncated.mha")) + { + std::cout << "Truncated compressed data reported success: FAIL" << '\n'; + return EXIT_FAILURE; + } + } + + std::remove(localName); + std::remove("testMeta14_truncated.mha"); + + std::cout << "Compressed element data tests: PASS" << '\n'; + return EXIT_SUCCESS; +} diff --git a/src/tests/testMeta2Object.cxx b/src/tests/testMeta2Object.cxx index c79c6f15de7..b1385ad061d 100644 --- a/src/tests/testMeta2Object.cxx +++ b/src/tests/testMeta2Object.cxx @@ -40,25 +40,31 @@ main(int, char *[]) float myMatrix[4]; for (i = 0; i < 4; i++) { - myMatrix[i] = i; + myMatrix[i] = static_cast(i); } tObj.AddUserField("MyMatrix", MET_FLOAT_MATRIX, 2, myMatrix); + std::cout << "*** Writing this info..." << std::endl; tObj.PrintInfo(); tObj.Write(); tObj.Clear(); tObj.ClearUserFields(); + std::cout << "*** Adding user fields..." << std::endl; tObj.AddUserField("MyName", MET_STRING); tObj.AddUserField("MyArray", MET_INT_ARRAY, 3); tObj.AddUserField("MyMatrix", MET_FLOAT_MATRIX, 2); + std::cout << "*** Pre-reading..." << std::endl; + tObj.PrintInfo(); + std::cout << "*** Reading..." << std::endl; tObj.Read(); + std::cout << "*** Reading results..." << std::endl; tObj.PrintInfo(); char * name = static_cast(tObj.GetUserField("MyName")); - if (strcmp(name, "Julien") != 0) + if (name == nullptr || strcmp(name, "Julien") != 0) { std::cout << "MyName: FAIL" << '\n'; return EXIT_FAILURE; @@ -69,7 +75,7 @@ main(int, char *[]) int * array = static_cast(tObj.GetUserField("MyArray")); for (i = 0; i < 3; i++) { - if (array[i] != i + 1) + if (array == nullptr || array[i] != i + 1) { std::cout << "MyArray: FAIL" << '\n'; delete[] array; @@ -82,7 +88,7 @@ main(int, char *[]) auto * matrix = static_cast(tObj.GetUserField("MyMatrix")); for (i = 0; i < 4; i++) { - if (matrix[i] != i) + if (matrix == nullptr || matrix[i] != i) { std::cout << "MyMatrix: FAIL" << '\n'; delete[] matrix; diff --git a/src/tests/testMeta4Tube.cxx b/src/tests/testMeta4Tube.cxx index 5e41df30224..e5fffcde703 100644 --- a/src/tests/testMeta4Tube.cxx +++ b/src/tests/testMeta4Tube.cxx @@ -25,10 +25,11 @@ main(int, char *[]) for (i = 0; i < 10; i++) { pnt = new TubePnt(3); - pnt->m_X[0] = i; - pnt->m_X[1] = i; - pnt->m_X[2] = i; - pnt->m_R = i; + float i_f = static_cast(i); + pnt->m_X[0] = i_f; + pnt->m_X[1] = i_f; + pnt->m_X[2] = i_f; + pnt->m_R = i_f; tube1->GetPoints().push_back(pnt); } @@ -38,10 +39,11 @@ main(int, char *[]) for (i = 0; i < 5; i++) { pnt = new TubePnt(3); - pnt->m_X[0] = i; - pnt->m_X[1] = i; - pnt->m_X[2] = i; - pnt->m_R = i; + float i_f = static_cast(i); + pnt->m_X[0] = i_f; + pnt->m_X[1] = i_f; + pnt->m_X[2] = i_f; + pnt->m_R = i_f; tube2->GetPoints().push_back(pnt); } diff --git a/src/tests/testMeta5Blob.cxx b/src/tests/testMeta5Blob.cxx index 0772304173f..33e3da1b6a0 100644 --- a/src/tests/testMeta5Blob.cxx +++ b/src/tests/testMeta5Blob.cxx @@ -20,8 +20,8 @@ main(int, char *[]) { pnt = new BlobPnt(3); pnt->m_X[0] = static_cast(0.2); - pnt->m_X[1] = i; - pnt->m_X[2] = i; + pnt->m_X[1] = static_cast(i); + pnt->m_X[2] = static_cast(i); blob.GetPoints().push_back(pnt); } diff --git a/src/tests/testMeta6Surface.cxx b/src/tests/testMeta6Surface.cxx index 116501d31e2..cf4db0eedfa 100644 --- a/src/tests/testMeta6Surface.cxx +++ b/src/tests/testMeta6Surface.cxx @@ -15,11 +15,12 @@ main(int, char *[]) { pnt = new SurfacePnt(3); pnt->m_X[0] = static_cast(0.2); - pnt->m_X[1] = i; - pnt->m_X[2] = i; + float i_f = static_cast(i); + pnt->m_X[1] = i_f; + pnt->m_X[2] = i_f; pnt->m_V[0] = static_cast(0.8); - pnt->m_V[1] = i; - pnt->m_V[2] = i; + pnt->m_V[1] = i_f; + pnt->m_V[2] = i_f; surface->GetPoints().push_back(pnt); } diff --git a/src/tests/testMeta7Line.cxx b/src/tests/testMeta7Line.cxx index 32ecec0f878..012243c94d5 100644 --- a/src/tests/testMeta7Line.cxx +++ b/src/tests/testMeta7Line.cxx @@ -15,14 +15,15 @@ main(int, char *[]) { pnt = new LinePnt(3); pnt->m_X[0] = static_cast(0.2); - pnt->m_X[1] = i; - pnt->m_X[2] = i; + float i_f = static_cast(i); + pnt->m_X[1] = i_f; + pnt->m_X[2] = i_f; pnt->m_V[0][0] = static_cast(0.3); - pnt->m_V[0][1] = i; - pnt->m_V[0][2] = i; + pnt->m_V[0][1] = i_f; + pnt->m_V[0][2] = i_f; pnt->m_V[1][0] = static_cast(0.4); - pnt->m_V[1][1] = i + 1; - pnt->m_V[1][2] = i + 1; + pnt->m_V[1][1] = i_f + 1; + pnt->m_V[1][2] = i_f + 1; Line->GetPoints().push_back(pnt); } diff --git a/src/tests/testMeta9Landmark.cxx b/src/tests/testMeta9Landmark.cxx index d53293ca23c..5912775aaf7 100644 --- a/src/tests/testMeta9Landmark.cxx +++ b/src/tests/testMeta9Landmark.cxx @@ -16,8 +16,8 @@ main(int, char *[]) { pnt = new LandmarkPnt(3); pnt->m_X[0] = static_cast(0.2); - pnt->m_X[1] = i; - pnt->m_X[2] = i; + pnt->m_X[1] = static_cast(i); + pnt->m_X[2] = static_cast(i); Landmark.GetPoints().push_back(pnt); }