From 964185b4a1a9e985744703c76e562f8e6bb4e7af Mon Sep 17 00:00:00 2001 From: ptahmose Date: Fri, 17 Feb 2023 00:00:33 +0100 Subject: [PATCH 01/13] clang-tidy fixes --- Src/libCZI/CziWriter.cpp | 4 ++-- Src/libCZI/splines.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Src/libCZI/CziWriter.cpp b/Src/libCZI/CziWriter.cpp index 90e79f18..b354c921 100644 --- a/Src/libCZI/CziWriter.cpp +++ b/Src/libCZI/CziWriter.cpp @@ -586,7 +586,7 @@ void libCZI::ICziWriter::SyncAddSubBlock(const AddSubBlockInfoStridedBitmap& add uint64_t bytesWritten; uint64_t totalBytesWritten = 0; - auto msHeaderAllocatedSize = ms.header.AllocatedSize; // need to save this information before (potentially) changing the byte-order + const auto msHeaderAllocatedSize = ms.header.AllocatedSize; // need to save this information before (potentially) changing the byte-order ConvertToHostByteOrder::Convert(&ms); info.writeFunc(metadataSegmentPos, &ms, sizeof(ms), &bytesWritten, "MetadataSegment"); @@ -606,7 +606,7 @@ void libCZI::ICziWriter::SyncAddSubBlock(const AddSubBlockInfoStridedBitmap& add if (totalBytesWritten < msHeaderAllocatedSize + sizeof(SegmentHeader)) { - totalBytesWritten += CWriterUtils::WriteZeroes(info.writeFunc, metadataSegmentPos + totalBytesWritten, msHeaderAllocatedSize + sizeof(SegmentHeader) - totalBytesWritten); + CWriterUtils::WriteZeroes(info.writeFunc, metadataSegmentPos + totalBytesWritten, msHeaderAllocatedSize + sizeof(SegmentHeader) - totalBytesWritten); } return make_tuple(metadataSegmentPos, static_cast(msHeaderAllocatedSize)); diff --git a/Src/libCZI/splines.cpp b/Src/libCZI/splines.cpp index 9f0ba6b5..c1952497 100644 --- a/Src/libCZI/splines.cpp +++ b/Src/libCZI/splines.cpp @@ -117,7 +117,7 @@ using namespace Eigen; // TODO: since the points are sorted for x (I'd think so...) we should be able to use a binary search here? int index = 0; - double xPos_for_foundIndex; + double xPos_for_foundIndex = 0; for (int i = 0; i < pointsCnt; i++) { double xPos_i; From c3d0b0813dbea261bfcbace1872b775c48c6d9c1 Mon Sep 17 00:00:00 2001 From: ptahmose Date: Fri, 17 Feb 2023 20:22:20 +0100 Subject: [PATCH 02/13] cosmetic --- Src/libCZI/CziParse.cpp | 16 ++++++++-------- Src/libCZI/CziReaderWriter.cpp | 2 +- Src/libCZI/CziSubBlockDirectory.cpp | 4 ++-- Src/libCZI/CziWriter.cpp | 3 +-- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Src/libCZI/CziParse.cpp b/Src/libCZI/CziParse.cpp index f33f13c4..93c8a9ba 100644 --- a/Src/libCZI/CziParse.cpp +++ b/Src/libCZI/CziParse.cpp @@ -214,7 +214,7 @@ using namespace libCZI; } catch (const std::exception&) { - std::throw_with_nested(LibCZIIOException("Error reading FileHeaderSegement", offset + sizeof(attachmentDirSegment), attachmentEntriesSize)); + std::throw_with_nested(LibCZIIOException("Error reading FileHeaderSegment", offset + sizeof(attachmentDirSegment), attachmentEntriesSize)); } if (bytesRead != attachmentEntriesSize) @@ -378,7 +378,7 @@ using namespace libCZI; } catch (const std::exception&) { - std::throw_with_nested(LibCZIIOException("Error reading FileHeaderSegement", offset + lengthSubblockSegmentData + sizeof(SegmentHeader), subBlckSegment.data.MetadataSize)); + std::throw_with_nested(LibCZIIOException("Error reading FileHeaderSegment", offset + lengthSubblockSegmentData + sizeof(SegmentHeader), subBlckSegment.data.MetadataSize)); } if (bytesRead != subBlckSegment.data.MetadataSize) @@ -395,7 +395,7 @@ using namespace libCZI; } catch (const std::exception&) { - std::throw_with_nested(LibCZIIOException("Error reading FileHeaderSegement", offset + lengthSubblockSegmentData + sizeof(SegmentHeader) + subBlckSegment.data.MetadataSize, subBlckSegment.data.DataSize)); + std::throw_with_nested(LibCZIIOException("Error reading FileHeaderSegment", offset + lengthSubblockSegmentData + sizeof(SegmentHeader) + subBlckSegment.data.MetadataSize, subBlckSegment.data.DataSize)); } if (bytesRead != subBlckSegment.data.DataSize) @@ -412,7 +412,7 @@ using namespace libCZI; } catch (const std::exception&) { - std::throw_with_nested(LibCZIIOException("Error reading FileHeaderSegement", offset + lengthSubblockSegmentData + sizeof(SegmentHeader) + subBlckSegment.data.MetadataSize + subBlckSegment.data.DataSize, subBlckSegment.data.AttachmentSize)); + std::throw_with_nested(LibCZIIOException("Error reading FileHeaderSegment", offset + lengthSubblockSegmentData + sizeof(SegmentHeader) + subBlckSegment.data.MetadataSize + subBlckSegment.data.DataSize, subBlckSegment.data.AttachmentSize)); } if (bytesRead != subBlckSegment.data.AttachmentSize) @@ -467,7 +467,7 @@ using namespace libCZI; } catch (const std::exception&) { - std::throw_with_nested(LibCZIIOException("Error reading AttachmentSegement", offset + 256 + sizeof(SegmentHeader), attchmntSegment.data.DataSize)); + std::throw_with_nested(LibCZIIOException("Error reading AttachmentSegment", offset + 256 + sizeof(SegmentHeader), attchmntSegment.data.DataSize)); } if (bytesRead != attchmntSegment.data.DataSize) @@ -566,7 +566,7 @@ using namespace libCZI; } catch (const std::exception&) { - std::throw_with_nested(LibCZIIOException("Error reading MetaDataSegement", offset, sizeof(metadataSegment))); + std::throw_with_nested(LibCZIIOException("Error reading MetaDataSegment", offset, sizeof(metadataSegment))); } if (bytesRead != sizeof(metadataSegment)) @@ -593,7 +593,7 @@ using namespace libCZI; } catch (const std::exception&) { - std::throw_with_nested(LibCZIIOException("Error reading MetaDataSegement", offset + sizeof(metadataSegment), metadataSegment.data.XmlSize)); + std::throw_with_nested(LibCZIIOException("Error reading MetaDataSegment", offset + sizeof(metadataSegment), metadataSegment.data.XmlSize)); } if (bytesRead != metadataSegment.data.XmlSize) @@ -610,7 +610,7 @@ using namespace libCZI; } catch (const std::exception&) { - std::throw_with_nested(LibCZIIOException("Error reading MetaDataSegement", offset + sizeof(metadataSegment) + metadataSegment.data.XmlSize, metadataSegment.data.AttachmentSize)); + std::throw_with_nested(LibCZIIOException("Error reading MetaDataSegment", offset + sizeof(metadataSegment) + metadataSegment.data.XmlSize, metadataSegment.data.AttachmentSize)); } if (bytesRead != metadataSegment.data.AttachmentSize) diff --git a/Src/libCZI/CziReaderWriter.cpp b/Src/libCZI/CziReaderWriter.cpp index 2f3f58aa..f0bbc5c3 100644 --- a/Src/libCZI/CziReaderWriter.cpp +++ b/Src/libCZI/CziReaderWriter.cpp @@ -33,7 +33,7 @@ struct ReplaceHelper int key; ICziReaderWriter* t; ReplaceHelper(int key, ICziReaderWriter* t) - :key(key), t(t) {}; + :key(key), t(t) {} void operator()(const AddSubBlockInfo& addSbBlkInfo) const { diff --git a/Src/libCZI/CziSubBlockDirectory.cpp b/Src/libCZI/CziSubBlockDirectory.cpp index 45395a0c..f2f2f3b8 100644 --- a/Src/libCZI/CziSubBlockDirectory.cpp +++ b/Src/libCZI/CziSubBlockDirectory.cpp @@ -149,12 +149,12 @@ void CSbBlkStatisticsUpdater::UpdateStatistics(const CCziSubBlockDirectoryBase:: auto it = this->pyramidStatistics.scenePyramidStatistics.find(sceneIndex); if (it != this->pyramidStatistics.scenePyramidStatistics.end()) { - this->UpdatePyramidLayerStatistics(it->second, pli); + CSbBlkStatisticsUpdater::UpdatePyramidLayerStatistics(it->second, pli); } else { std::vector vecPs; - this->UpdatePyramidLayerStatistics(vecPs, pli); + CSbBlkStatisticsUpdater::UpdatePyramidLayerStatistics(vecPs, pli); this->pyramidStatistics.scenePyramidStatistics.insert(std::pair>(sceneIndex, vecPs)); } diff --git a/Src/libCZI/CziWriter.cpp b/Src/libCZI/CziWriter.cpp index b354c921..779f8a08 100644 --- a/Src/libCZI/CziWriter.cpp +++ b/Src/libCZI/CziWriter.cpp @@ -54,7 +54,7 @@ void libCZI::ICziWriter::SyncAddSubBlock(const libCZI::AddSubBlockInfoLinewiseBi { AddSubBlockInfo addSbInfo(addSbInfoLinewise); - size_t stride = addSbInfoLinewise.physicalWidth * CziUtils::GetBytesPerPel(addSbInfoLinewise.PixelType); + size_t stride = addSbInfoLinewise.physicalWidth * (size_t)CziUtils::GetBytesPerPel(addSbInfoLinewise.PixelType); addSbInfo.sizeData = addSbInfoLinewise.physicalHeight * stride; auto linesCnt = addSbInfoLinewise.physicalHeight; addSbInfo.getData = [&](int callCnt, size_t offset, const void*& ptr, size_t& size)->bool @@ -699,7 +699,6 @@ void libCZI::ICziWriter::SyncAddSubBlock(const AddSubBlockInfoStridedBitmap& add uint64_t attchmDirPos; // if we have already written a subblock-directory-segment (possibly a reservation), then we check here if the existing // segment is large enough, and if so we write our data into this segment - //if (this->attachmentDirectorySegment.GetAllocatedSize() >= attchmntDirSegment.header.UsedSize) if (int64_t(info.sizeExistingSegmentPos) >= attchmntDirSegment.header.UsedSize) { attchmDirPos = info.existingSegmentPos;// this->attachmentDirectorySegment.GetFilePos(); From 7345030c4fbb9698d404ab97dfb85768f121dcb2 Mon Sep 17 00:00:00 2001 From: ptahmose Date: Sat, 10 Feb 2024 01:09:18 +0100 Subject: [PATCH 03/13] some fixes for czireaderwriter --- Src/libCZI/CziReaderWriter.cpp | 21 ++++++++++++--------- Src/libCZI/CziWriter.cpp | 4 +++- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Src/libCZI/CziReaderWriter.cpp b/Src/libCZI/CziReaderWriter.cpp index ffe83669..2a57a7dd 100644 --- a/Src/libCZI/CziReaderWriter.cpp +++ b/Src/libCZI/CziReaderWriter.cpp @@ -130,8 +130,8 @@ void ICziReaderWriter::ReplaceSubBlock(int key, const libCZI::AddSubBlockInfoStr wi.writeFunc = std::bind(&CCziReaderWriter::WriteToOutputStream, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4, placeholders::_5); wi.useSpecifiedAllocatedSize = false; - auto sizeOfSbBlk = CWriterUtils::WriteSubBlock(wi, addSbBlkInfo); - this->nextSegmentInfo.SetNextSegmentPos(wi.segmentPos + sizeOfSbBlk /*+ sizeof(SegmentHeader)*/); + const auto sizeOfSbBlk = CWriterUtils::WriteSubBlock(wi, addSbBlkInfo); + this->nextSegmentInfo.SetNextSegmentPos(wi.segmentPos + sizeOfSbBlk); } /*virtual*/void CCziReaderWriter::SyncAddAttachment(const libCZI::AddAttachmentInfo& addAttachmentInfo) @@ -415,13 +415,14 @@ void CCziReaderWriter::ReadCziStructure() }, &attchmntDirSegmentSize); + this->attachmentDirectory.SetModified(false); this->attachmentDirectorySegment.SetPositionAndAllocatedSize(pos, attchmntDirSegmentSize.AllocatedSize, false); } pos = this->hdrSegmentData.GetMetadataPosition(); if (pos != 0) { - auto segmentSize = CCZIParse::ReadSegmentHeader(CCZIParse::SegmentType::Metadata, this->stream.get(), this->hdrSegmentData.GetMetadataPosition()); + const auto segmentSize = CCZIParse::ReadSegmentHeader(CCZIParse::SegmentType::Metadata, this->stream.get(), this->hdrSegmentData.GetMetadataPosition()); this->metadataSegment.SetPositionAndAllocatedSize(pos, segmentSize.AllocatedSize, false); } } @@ -641,12 +642,14 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, [&](int index, const CCziSubBlockDirectory::SubBlkEntry& entry)->bool { SubBlockInfo info; - info.coordinate = entry.coordinate; - info.logicalRect = IntRect{ entry.x,entry.y,entry.width,entry.height }; - info.physicalSize = IntSize{ (std::uint32_t)entry.storedWidth, (std::uint32_t)entry.storedHeight }; - info.mIndex = entry.mIndex; - info.pixelType = CziUtils::PixelTypeFromInt(entry.PixelType); - return funcEnum(index, info); + info.compressionModeRaw = entry.Compression; + info.pixelType = CziUtils::PixelTypeFromInt(entry.PixelType); + info.coordinate = entry.coordinate; + info.logicalRect = IntRect{ entry.x,entry.y,entry.width,entry.height }; + info.physicalSize = IntSize{ (std::uint32_t)entry.storedWidth, (std::uint32_t)entry.storedHeight }; + info.mIndex = entry.mIndex; + info.pyramidType = CziUtils::PyramidTypeFromByte(entry.pyramid_type_from_spare); + return funcEnum(index, info); }); } diff --git a/Src/libCZI/CziWriter.cpp b/Src/libCZI/CziWriter.cpp index 767bd180..5981e920 100644 --- a/Src/libCZI/CziWriter.cpp +++ b/Src/libCZI/CziWriter.cpp @@ -733,10 +733,11 @@ void libCZI::ICziWriter::SyncAddSubBlock(const AddSubBlockInfoStridedBitmap& add std::unique_ptr upAttchmntData((AttachmentEntryA1*)malloc(sizeAttchmntEntries), [](AttachmentEntryA1* p)->void {free(p); }); + size_t index_count = 0; info.enumEntriesFunc( [&](size_t index, const CCziAttachmentsDirectoryBase::AttachmentEntry& entry)->bool { - AttachmentEntryA1* p = (upAttchmntData.get() + index); + AttachmentEntryA1* p = upAttchmntData.get() + index_count; p->SchemaType[0] = 'A'; p->SchemaType[1] = '1'; memset(&p->_spare[0], 0, sizeof(p->_spare)); @@ -747,6 +748,7 @@ void libCZI::ICziWriter::SyncAddSubBlock(const AddSubBlockInfoStridedBitmap& add memcpy(&p->Name[0], &entry.Name[0], sizeof(p->Name)); ConvertToHostByteOrder::Convert(p); + ++index_count; return true; }); From a341b3e91828053bdda475c0c7f9cb359dda4691 Mon Sep 17 00:00:00 2001 From: ptahmose Date: Sat, 10 Feb 2024 01:14:28 +0100 Subject: [PATCH 04/13] bump version --- CMakeLists.txt | 2 +- Src/libCZI/Doc/version-history.markdown | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5432c0c5..0b6251f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.15) cmake_policy(SET CMP0091 NEW) # enable new "MSVC runtime library selection" (https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html) project(libCZI - VERSION 0.58.0 + VERSION 0.58.1 HOMEPAGE_URL "https://github.com/ZEISS/libczi" DESCRIPTION "libCZI is an Open Source Cross-Platform C++ library to read and write CZI") diff --git a/Src/libCZI/Doc/version-history.markdown b/Src/libCZI/Doc/version-history.markdown index 0d6c74ef..8e6f3183 100644 --- a/Src/libCZI/Doc/version-history.markdown +++ b/Src/libCZI/Doc/version-history.markdown @@ -17,3 +17,4 @@ version history {#version_history} 0.57.2 | [90](https://github.com/ZEISS/libczi/pull/90) | improve thread-safety of CziReader 0.57.3 | [91](https://github.com/ZEISS/libczi/pull/91) | improve error-message 0.58.0 | [92](https://github.com/ZEISS/libczi/pull/92) | export a list with properties for streams-property-bag + 0.58.1 | [93](https://github.com/ZEISS/libczi/pull/93) | some fixes for CziReaderWriter \ No newline at end of file From bcac93250dc4de97ae66e26c9d023a8a931be3d1 Mon Sep 17 00:00:00 2001 From: ptahmose Date: Sat, 10 Feb 2024 01:17:20 +0100 Subject: [PATCH 05/13] fix link --- Src/libCZI/Doc/version-history.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/libCZI/Doc/version-history.markdown b/Src/libCZI/Doc/version-history.markdown index 8e6f3183..ca025360 100644 --- a/Src/libCZI/Doc/version-history.markdown +++ b/Src/libCZI/Doc/version-history.markdown @@ -17,4 +17,4 @@ version history {#version_history} 0.57.2 | [90](https://github.com/ZEISS/libczi/pull/90) | improve thread-safety of CziReader 0.57.3 | [91](https://github.com/ZEISS/libczi/pull/91) | improve error-message 0.58.0 | [92](https://github.com/ZEISS/libczi/pull/92) | export a list with properties for streams-property-bag - 0.58.1 | [93](https://github.com/ZEISS/libczi/pull/93) | some fixes for CziReaderWriter \ No newline at end of file + 0.58.1 | [95](https://github.com/ZEISS/libczi/pull/95) | some fixes for CziReaderWriter \ No newline at end of file From 50ec6a54cf248b97d4b9aa7d3ca2a896bd03e4fa Mon Sep 17 00:00:00 2001 From: ptahmose Date: Sun, 11 Feb 2024 02:53:42 +0100 Subject: [PATCH 06/13] implement some missing methods --- Src/libCZI/CMakeLists.txt | 2 + Src/libCZI/CZIReader.cpp | 77 ++++++++------- Src/libCZI/CziReaderCommon.cpp | 103 +++++++++++++++++++++ Src/libCZI/CziReaderCommon.h | 28 ++++++ Src/libCZI/CziReaderWriter.cpp | 23 +++-- Src/libCZI_UnitTests/test_readerwriter.cpp | 40 +++++++- 6 files changed, 229 insertions(+), 44 deletions(-) create mode 100644 Src/libCZI/CziReaderCommon.cpp create mode 100644 Src/libCZI/CziReaderCommon.h diff --git a/Src/libCZI/CMakeLists.txt b/Src/libCZI/CMakeLists.txt index 629e02fb..de1494b9 100644 --- a/Src/libCZI/CMakeLists.txt +++ b/Src/libCZI/CMakeLists.txt @@ -20,6 +20,7 @@ set(LIBCZISRCFILES CziMetadataSegment.cpp CziParse.cpp CZIReader.cpp + CziReaderCommon.cpp CziReaderWriter.cpp CziStructs.cpp CziSubBlock.cpp @@ -60,6 +61,7 @@ set(LIBCZISRCFILES CziMetadataDocumentInfo2.h CziMetadataSegment.h CziParse.h + CziReaderCommon.h CZIReader.h CziReaderWriter.h CziStructs.h diff --git a/Src/libCZI/CZIReader.cpp b/Src/libCZI/CZIReader.cpp index 8f09f75c..2423e328 100644 --- a/Src/libCZI/CZIReader.cpp +++ b/Src/libCZI/CZIReader.cpp @@ -10,6 +10,7 @@ #include "CziUtils.h" #include "utilities.h" #include "CziAttachment.h" +#include "CziReaderCommon.h" using namespace std; using namespace libCZI; @@ -136,6 +137,8 @@ CCZIReader::CCZIReader() : isOperational(false) { this->ThrowIfNotOperational(); + CziReaderCommon::EnumSubset(this, planeCoordinate, roi, onlyLayer0, funcEnum); + /* // TODO: // Ok... for a first tentative, experimental and quick-n-dirty implementation, simply // walk through all the subblocks. We surely want to have something more elaborated @@ -159,6 +162,7 @@ CCZIReader::CCZIReader() : isOperational(false) return true; }); + */ } /*virtual*/std::shared_ptr CCZIReader::ReadSubBlock(int index) @@ -176,41 +180,42 @@ CCZIReader::CCZIReader() : isOperational(false) /*virtual*/bool CCZIReader::TryGetSubBlockInfoOfArbitrarySubBlockInChannel(int channelIndex, SubBlockInfo& info) { this->ThrowIfNotOperational(); + return CziReaderCommon::TryGetSubBlockInfoOfArbitrarySubBlockInChannel(this, channelIndex, info); // TODO: we should be able to gather this information when constructing the subblock-list // for the time being... just walk through the whole list // - bool foundASubBlock = false; - SubBlockStatistics s = this->subBlkDir.GetStatistics(); - if (!s.dimBounds.IsValid(DimensionIndex::C)) - { - // in this case -> just take the first subblock... - this->EnumerateSubBlocks( - [&](int index, const SubBlockInfo& sbinfo)->bool - { - info = sbinfo; - foundASubBlock = true; - return false; - }); - } - else - { - this->EnumerateSubBlocks( - [&](int index, const SubBlockInfo& sbinfo)->bool - { - int c; - if (sbinfo.coordinate.TryGetPosition(DimensionIndex::C, &c) == true && c == channelIndex) - { - info = sbinfo; - foundASubBlock = true; - return false; - } - - return true; - }); - } - - return foundASubBlock; + //bool foundASubBlock = false; + //SubBlockStatistics s = this->subBlkDir.GetStatistics(); + //if (!s.dimBounds.IsValid(DimensionIndex::C)) + //{ + // // in this case -> just take the first subblock... + // this->EnumerateSubBlocks( + // [&](int index, const SubBlockInfo& sbinfo)->bool + // { + // info = sbinfo; + // foundASubBlock = true; + // return false; + // }); + //} + //else + //{ + // this->EnumerateSubBlocks( + // [&](int index, const SubBlockInfo& sbinfo)->bool + // { + // int c; + // if (sbinfo.coordinate.TryGetPosition(DimensionIndex::C, &c) == true && c == channelIndex) + // { + // info = sbinfo; + // foundASubBlock = true; + // return false; + // } + + // return true; + // }); + //} + + //return foundASubBlock; } /*virtual*/bool CCZIReader::TryGetSubBlockInfo(int index, SubBlockInfo* info) const @@ -273,7 +278,7 @@ CCZIReader::CCZIReader() : isOperational(false) /*virtual*/void CCZIReader::EnumerateSubset(const char* contentFileType, const char* name, const std::function& funcEnum) { this->ThrowIfNotOperational(); - libCZI::AttachmentInfo ai; + /* libCZI::AttachmentInfo ai; ai.contentFileType[sizeof(ai.contentFileType) - 1] = '\0'; this->attachmentDir.EnumAttachments( [&](int index, const CCziAttachmentsDirectory::AttachmentEntry& ae) @@ -291,7 +296,13 @@ CCZIReader::CCZIReader() : isOperational(false) } return true; - }); + });*/ + CziReaderCommon::EnumerateSubset( + //this->attachmentDir.EnumAttachments, + std::bind(&CCziAttachmentsDirectory::EnumAttachments, &this->attachmentDir, std::placeholders::_1), + contentFileType, + name, + funcEnum); } /*virtual*/std::shared_ptr CCZIReader::ReadAttachment(int index) diff --git a/Src/libCZI/CziReaderCommon.cpp b/Src/libCZI/CziReaderCommon.cpp new file mode 100644 index 00000000..3b079993 --- /dev/null +++ b/Src/libCZI/CziReaderCommon.cpp @@ -0,0 +1,103 @@ +#include "CziReaderCommon.h" + +#include "CziUtils.h" +#include "utilities.h" + +using namespace std; +using namespace libCZI; + +/*static*/void CziReaderCommon::EnumSubset( + libCZI::ISubBlockRepository* repository, + const libCZI::IDimCoordinate* planeCoordinate, + const libCZI::IntRect* roi, + bool onlyLayer0, + const std::function& funcEnum) +{ + // Ok... for a first tentative, experimental and quick-n-dirty implementation, simply + // walk through all the subblocks. We surely want to have something more elaborated + // here. + repository->EnumerateSubBlocks( + [&](int index, const SubBlockInfo& info)->bool + { + // TODO: we only deal with layer 0 currently... or, more precisely, we do not take "zoom" into account at all + // -> well... added that boolean "onlyLayer0" - is this sufficient...? + if (onlyLayer0 == false || (info.physicalSize.w == info.logicalRect.w && info.physicalSize.h == info.logicalRect.h)) + { + if (planeCoordinate == nullptr || CziUtils::CompareCoordinate(planeCoordinate, &info.coordinate) == true) + { + if (roi == nullptr || Utilities::DoIntersect(*roi, info.logicalRect)) + { + bool b = funcEnum(index, info); + return b; + } + } + } + + return true; + }); +} + +/*static*/bool CziReaderCommon::TryGetSubBlockInfoOfArbitrarySubBlockInChannel( + libCZI::ISubBlockRepository* repository, + int channelIndex, + libCZI::SubBlockInfo& info) +{ + bool foundASubBlock = false; + SubBlockStatistics s = repository->GetStatistics();// this->subBlkDir.GetStatistics(); + if (!s.dimBounds.IsValid(DimensionIndex::C)) + { + // in this case -> just take the first subblock... + repository->EnumerateSubBlocks( + [&](int index, const SubBlockInfo& sbinfo)->bool + { + info = sbinfo; + foundASubBlock = true; + return false; + }); + } + else + { + repository->EnumerateSubBlocks( + [&](int index, const SubBlockInfo& sbinfo)->bool + { + int c; + if (sbinfo.coordinate.TryGetPosition(DimensionIndex::C, &c) == true && c == channelIndex) + { + info = sbinfo; + foundASubBlock = true; + return false; + } + + return true; + }); + } + + return foundASubBlock; +} + +/*static*/void CziReaderCommon::EnumerateSubset( + const std::function&)>& func, + const char* contentFileType, + const char* name, + const std::function& funcEnum) +{ + libCZI::AttachmentInfo ai; + ai.contentFileType[sizeof(ai.contentFileType) - 1] = '\0'; + func( + [&](int index, const CCziAttachmentsDirectoryBase::AttachmentEntry& ae) + { + if (contentFileType == nullptr || strcmp(contentFileType, ae.ContentFileType) == 0) + { + if (name == nullptr || strcmp(name, ae.Name) == 0) + { + ai.contentGuid = ae.ContentGuid; + memcpy(ai.contentFileType, ae.ContentFileType, sizeof(ae.ContentFileType)); + ai.name = ae.Name; + bool b = funcEnum(index, ai); + return b; + } + } + + return true; + }); +} diff --git a/Src/libCZI/CziReaderCommon.h b/Src/libCZI/CziReaderCommon.h new file mode 100644 index 00000000..37fe49cb --- /dev/null +++ b/Src/libCZI/CziReaderCommon.h @@ -0,0 +1,28 @@ +#pragma once + +#include "libCZI.h" +#include "CziAttachment.h" +#include + +class CziReaderCommon +{ +public: + static void EnumSubset( + libCZI::ISubBlockRepository* repository, + const libCZI::IDimCoordinate* planeCoordinate, + const libCZI::IntRect* roi, + bool onlyLayer0, + const std::function& funcEnum); + + static bool TryGetSubBlockInfoOfArbitrarySubBlockInChannel( + libCZI::ISubBlockRepository* repository, + int channelIndex, + libCZI::SubBlockInfo& info); + + static void EnumerateSubset( + const std::function&)>& func, + const char* contentFileType, + const char* name, + const std::function& funcEnum); + +}; diff --git a/Src/libCZI/CziReaderWriter.cpp b/Src/libCZI/CziReaderWriter.cpp index 2a57a7dd..e779e3ea 100644 --- a/Src/libCZI/CziReaderWriter.cpp +++ b/Src/libCZI/CziReaderWriter.cpp @@ -11,6 +11,7 @@ #include "utilities.h" #include "CziSubBlock.h" #include "CziAttachment.h" +#include "CziReaderCommon.h" using namespace libCZI; using namespace std; @@ -656,7 +657,7 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, /*virtual*/void CCziReaderWriter::EnumSubset(const libCZI::IDimCoordinate* planeCoordinate, const libCZI::IntRect* roi, bool onlyLayer0, const std::function& funcEnum) { this->ThrowIfNotOperational(); - throw std::runtime_error("Not Implemented"); + CziReaderCommon::EnumSubset(this, planeCoordinate, roi, onlyLayer0, funcEnum); } /*virtual*/std::shared_ptr CCziReaderWriter::ReadSubBlock(int index) @@ -710,7 +711,8 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, /*virtual*/bool CCziReaderWriter::TryGetSubBlockInfoOfArbitrarySubBlockInChannel(int channelIndex, libCZI::SubBlockInfo& info) { this->ThrowIfNotOperational(); - throw std::runtime_error("Not Implemented"); + return CziReaderCommon::TryGetSubBlockInfoOfArbitrarySubBlockInChannel(this, channelIndex, info); + // throw std::runtime_error("Not Implemented"); } /*virtual*/libCZI::SubBlockStatistics CCziReaderWriter::GetStatistics() @@ -732,18 +734,23 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, [&](int index, const CCziAttachmentsDirectoryBase::AttachmentEntry& entry)->bool { libCZI::AttachmentInfo info; - info.contentGuid = entry.ContentGuid; - memcpy(info.contentFileType, entry.ContentFileType, sizeof(entry.ContentFileType)); - info.name = entry.Name; - bool b = funcEnum(index, info); - return b; + info.contentGuid = entry.ContentGuid; + memcpy(info.contentFileType, entry.ContentFileType, sizeof(entry.ContentFileType)); + info.name = entry.Name; + bool b = funcEnum(index, info); + return b; }); } /*virtual*/void CCziReaderWriter::EnumerateSubset(const char* contentFileType, const char* name, const std::function& funcEnum) { this->ThrowIfNotOperational(); - throw std::runtime_error("Not Implemented"); + //throw std::runtime_error("Not Implemented"); + CziReaderCommon::EnumerateSubset( + std::bind(&CReaderWriterCziAttachmentsDirectory::EnumEntries, &this->attachmentDirectory, std::placeholders::_1), + contentFileType, + name, + funcEnum); } /*virtual*/std::shared_ptr CCziReaderWriter::ReadAttachment(int index) diff --git a/Src/libCZI_UnitTests/test_readerwriter.cpp b/Src/libCZI_UnitTests/test_readerwriter.cpp index 9e2c9414..6819d91f 100644 --- a/Src/libCZI_UnitTests/test_readerwriter.cpp +++ b/Src/libCZI_UnitTests/test_readerwriter.cpp @@ -8,6 +8,7 @@ #include "utils.h" #include "MemInputOutputStream.h" #include "SegmentWalker.h" +#include using namespace libCZI; using namespace std; @@ -1349,9 +1350,9 @@ INSTANTIATE_TEST_SUITE_P( CziReaderWriter, SparePyramidTypeFixture, testing::Values( - SubBlockPyramidType::None, - SubBlockPyramidType::SingleSubBlock, - SubBlockPyramidType::MultiSubBlock)); + SubBlockPyramidType::None, + SubBlockPyramidType::SingleSubBlock, + SubBlockPyramidType::MultiSubBlock)); TEST(CziReaderWriter, TryAddingDuplicateAttachmentToCziReaderWriterAndExpectError) { @@ -1387,3 +1388,36 @@ TEST(CziReaderWriter, TryAddingDuplicateAttachmentToCziReaderWriterAndExpectErro add_attachment_info.contentGuid = GUID{ 111, 2, 3, {4, 5, 6, 7, 8, 9, 10, 11} }; reader_writer->SyncAddAttachment(add_attachment_info); } + +TEST(CziReaderWriter, TestEnumerateSubBlocks) +{ + auto testCzi = CreateTestCzi(); + + auto input_output_stream = make_shared(get<0>(testCzi).get(), get<1>(testCzi)); + auto rw = CreateCZIReaderWriter(); + rw->Create(input_output_stream); + + vector indices; + rw->EnumerateSubBlocks( + [&](int index, const SubBlockInfo& info)->bool + { + indices.push_back(index); + return true; + }); + + // Check the size + ASSERT_EQ(indices.size(), 50) << "Vector does not contain exactly 50 elements."; + + // Verify each number from 0 to 49 is present + bool allNumbersPresent = true; + for (int i = 0; i < 50; ++i) + { + if (std::find(indices.begin(), indices.end(), i) == indices.end()) + { + allNumbersPresent = false; + break; + } + } + + ASSERT_TRUE(allNumbersPresent) << "Not all numbers from 0 to 49 are present in the vector."; +} From 764db700bb8807f844f4ac637789dab5b82eb1f9 Mon Sep 17 00:00:00 2001 From: ptahmose Date: Sun, 11 Feb 2024 12:15:42 +0100 Subject: [PATCH 07/13] update --- Src/libCZI/CZIReader.cpp | 10 +++++---- Src/libCZI/CziReaderCommon.cpp | 13 +++++++++++ Src/libCZI/CziReaderCommon.h | 3 ++- Src/libCZI/CziReaderWriter.cpp | 18 +++++++++------- Src/libCZI_UnitTests/test_readerwriter.cpp | 25 ++++++++++++++++++++++ 5 files changed, 56 insertions(+), 13 deletions(-) diff --git a/Src/libCZI/CZIReader.cpp b/Src/libCZI/CZIReader.cpp index 2423e328..2b1cd0c9 100644 --- a/Src/libCZI/CZIReader.cpp +++ b/Src/libCZI/CZIReader.cpp @@ -102,7 +102,7 @@ CCZIReader::CCZIReader() : isOperational(false) this->subBlkDir.EnumSubBlocks( [&](int index, const CCziSubBlockDirectory::SubBlkEntry& entry)->bool { - SubBlockInfo info; + /*SubBlockInfo info; info.compressionModeRaw = entry.Compression; info.pixelType = CziUtils::PixelTypeFromInt(entry.PixelType); info.coordinate = entry.coordinate; @@ -110,7 +110,8 @@ CCZIReader::CCZIReader() : isOperational(false) info.physicalSize = IntSize{ (std::uint32_t)entry.storedWidth, (std::uint32_t)entry.storedHeight }; info.mIndex = entry.mIndex; info.pyramidType = CziUtils::PyramidTypeFromByte(entry.pyramid_type_from_spare); - return funcEnum(index, info); + return funcEnum(index, info);*/ + return funcEnum(index, CziReaderCommon::ConvertToSubBlockInfo(entry)); }); } @@ -228,13 +229,14 @@ CCZIReader::CCZIReader() : isOperational(false) if (info != nullptr) { - info->compressionModeRaw = entry.Compression; + *info = CziReaderCommon::ConvertToSubBlockInfo(entry); + /* info->compressionModeRaw = entry.Compression; info->pixelType = CziUtils::PixelTypeFromInt(entry.PixelType); info->coordinate = entry.coordinate; info->logicalRect = IntRect{ entry.x,entry.y,entry.width,entry.height }; info->physicalSize = IntSize{ static_cast(entry.storedWidth), static_cast(entry.storedHeight) }; info->mIndex = entry.mIndex; - info->pyramidType = CziUtils::PyramidTypeFromByte(entry.pyramid_type_from_spare); + info->pyramidType = CziUtils::PyramidTypeFromByte(entry.pyramid_type_from_spare);*/ } return true; diff --git a/Src/libCZI/CziReaderCommon.cpp b/Src/libCZI/CziReaderCommon.cpp index 3b079993..24030e72 100644 --- a/Src/libCZI/CziReaderCommon.cpp +++ b/Src/libCZI/CziReaderCommon.cpp @@ -101,3 +101,16 @@ using namespace libCZI; return true; }); } + +/*static*/libCZI::SubBlockInfo CziReaderCommon::ConvertToSubBlockInfo(const CCziSubBlockDirectory::SubBlkEntry& entry) +{ + SubBlockInfo info; + info.compressionModeRaw = entry.Compression; + info.pixelType = CziUtils::PixelTypeFromInt(entry.PixelType); + info.coordinate = entry.coordinate; + info.logicalRect = IntRect{ entry.x,entry.y,entry.width,entry.height }; + info.physicalSize = IntSize{ (std::uint32_t)entry.storedWidth, (std::uint32_t)entry.storedHeight }; + info.mIndex = entry.mIndex; + info.pyramidType = CziUtils::PyramidTypeFromByte(entry.pyramid_type_from_spare); + return info; +} diff --git a/Src/libCZI/CziReaderCommon.h b/Src/libCZI/CziReaderCommon.h index 37fe49cb..73e0b78f 100644 --- a/Src/libCZI/CziReaderCommon.h +++ b/Src/libCZI/CziReaderCommon.h @@ -24,5 +24,6 @@ class CziReaderCommon const char* contentFileType, const char* name, const std::function& funcEnum); - + + static libCZI::SubBlockInfo ConvertToSubBlockInfo(const CCziSubBlockDirectory::SubBlkEntry& entry); }; diff --git a/Src/libCZI/CziReaderWriter.cpp b/Src/libCZI/CziReaderWriter.cpp index e779e3ea..89f5925f 100644 --- a/Src/libCZI/CziReaderWriter.cpp +++ b/Src/libCZI/CziReaderWriter.cpp @@ -278,7 +278,7 @@ void CCziReaderWriter::Finish() }); }; sbBlkDirWriteInfo.writeFunc = std::bind(&CCziReaderWriter::WriteToOutputStream, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4, placeholders::_5); - auto posAndSize = CWriterUtils::WriteSubBlkDirectory(sbBlkDirWriteInfo); + const auto posAndSize = CWriterUtils::WriteSubBlkDirectory(sbBlkDirWriteInfo); this->subBlockDirectorySegment.SetPositionAndAllocatedSize(get<0>(posAndSize), get<1>(posAndSize), false); if (get<0>(posAndSize) == sbBlkDirWriteInfo.segmentPosForNewSegment) { @@ -312,11 +312,11 @@ void CCziReaderWriter::Finish() [&](size_t index, const CCziAttachmentsDirectoryBase::AttachmentEntry& e)->bool { f(index, e); - return true; + return true; }); }; attchmntDirWriteInfo.writeFunc = std::bind(&CCziReaderWriter::WriteToOutputStream, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4, placeholders::_5); - auto posAndSize = CWriterUtils::WriteAttachmentDirectory(attchmntDirWriteInfo); + const auto posAndSize = CWriterUtils::WriteAttachmentDirectory(attchmntDirWriteInfo); this->attachmentDirectorySegment.SetPositionAndAllocatedSize(get<0>(posAndSize), get<1>(posAndSize), false); if (get<0>(posAndSize) == attchmntDirWriteInfo.segmentPosForNewSegment) { @@ -381,7 +381,7 @@ void CCziReaderWriter::ReadCziStructure() if (this->info->GetForceFileGuid()) { // we then immediately update the File-Guid - auto newGuid = this->UpdateFileHeaderGuid(); + const auto newGuid = this->UpdateFileHeaderGuid(); memcpy(&fileHeaderSegment.FileGuid, &newGuid, sizeof(newGuid)); memcpy(&fileHeaderSegment.PrimaryFileGuid, &newGuid, sizeof(newGuid)); } @@ -642,7 +642,7 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, this->sbBlkDirectory.EnumEntries( [&](int index, const CCziSubBlockDirectory::SubBlkEntry& entry)->bool { - SubBlockInfo info; + /*SubBlockInfo info; info.compressionModeRaw = entry.Compression; info.pixelType = CziUtils::PixelTypeFromInt(entry.PixelType); info.coordinate = entry.coordinate; @@ -650,7 +650,8 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, info.physicalSize = IntSize{ (std::uint32_t)entry.storedWidth, (std::uint32_t)entry.storedHeight }; info.mIndex = entry.mIndex; info.pyramidType = CziUtils::PyramidTypeFromByte(entry.pyramid_type_from_spare); - return funcEnum(index, info); + return funcEnum(index, info);*/ + return funcEnum(index, CziReaderCommon::ConvertToSubBlockInfo(entry)); }); } @@ -696,13 +697,14 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, if (info != nullptr) { - info->compressionModeRaw = entry.Compression; + *info = CziReaderCommon::ConvertToSubBlockInfo(entry); + /*info->compressionModeRaw = entry.Compression; info->pixelType = CziUtils::PixelTypeFromInt(entry.PixelType); info->coordinate = entry.coordinate; info->logicalRect = IntRect{ entry.x,entry.y,entry.width,entry.height }; info->physicalSize = IntSize{ static_cast(entry.storedWidth), static_cast(entry.storedHeight) }; info->mIndex = entry.mIndex; - info->pyramidType = CziUtils::PyramidTypeFromByte(entry.pyramid_type_from_spare); + info->pyramidType = CziUtils::PyramidTypeFromByte(entry.pyramid_type_from_spare);*/ } return true; diff --git a/Src/libCZI_UnitTests/test_readerwriter.cpp b/Src/libCZI_UnitTests/test_readerwriter.cpp index 6819d91f..014fba4d 100644 --- a/Src/libCZI_UnitTests/test_readerwriter.cpp +++ b/Src/libCZI_UnitTests/test_readerwriter.cpp @@ -1420,4 +1420,29 @@ TEST(CziReaderWriter, TestEnumerateSubBlocks) } ASSERT_TRUE(allNumbersPresent) << "Not all numbers from 0 to 49 are present in the vector."; + + indices.clear(); + const auto query_rect = IntRect{ 8,0,8,1 }; + rw->EnumSubset(nullptr, &query_rect, true, + [&](int index, const SubBlockInfo& info)->bool + { + indices.push_back(index); + return true; + }); + + // Check the size + ASSERT_EQ(indices.size(), 20) << "Vector does not contain exactly 20 elements."; + + for (auto it = indices.begin(); it != indices.end(); ++it) + { + // check that the index is not present more than once + ASSERT_FALSE(std::find(it + 1, indices.end(), *it) != indices.end()); + + // check that the subblock is within the query rectangle + SubBlockInfo sub_block_info; + bool b = rw->TryGetSubBlockInfo(*it, &sub_block_info); + ASSERT_TRUE(b); + b = query_rect.IntersectsWith(sub_block_info.logicalRect); + ASSERT_TRUE(b); + } } From 9904b8b719fb2fdd916e97b577594a4ff77c7fce Mon Sep 17 00:00:00 2001 From: ptahmose Date: Sun, 11 Feb 2024 12:53:47 +0100 Subject: [PATCH 08/13] clean up CZIReader.cpp --- Src/libCZI/CZIReader.cpp | 97 ------------------------------------ Src/libCZI/CziReaderCommon.h | 1 + 2 files changed, 1 insertion(+), 97 deletions(-) diff --git a/Src/libCZI/CZIReader.cpp b/Src/libCZI/CZIReader.cpp index 2b1cd0c9..cb19dd10 100644 --- a/Src/libCZI/CZIReader.cpp +++ b/Src/libCZI/CZIReader.cpp @@ -102,15 +102,6 @@ CCZIReader::CCZIReader() : isOperational(false) this->subBlkDir.EnumSubBlocks( [&](int index, const CCziSubBlockDirectory::SubBlkEntry& entry)->bool { - /*SubBlockInfo info; - info.compressionModeRaw = entry.Compression; - info.pixelType = CziUtils::PixelTypeFromInt(entry.PixelType); - info.coordinate = entry.coordinate; - info.logicalRect = IntRect{ entry.x,entry.y,entry.width,entry.height }; - info.physicalSize = IntSize{ (std::uint32_t)entry.storedWidth, (std::uint32_t)entry.storedHeight }; - info.mIndex = entry.mIndex; - info.pyramidType = CziUtils::PyramidTypeFromByte(entry.pyramid_type_from_spare); - return funcEnum(index, info);*/ return funcEnum(index, CziReaderCommon::ConvertToSubBlockInfo(entry)); }); } @@ -137,33 +128,7 @@ CCZIReader::CCZIReader() : isOperational(false) /*virtual*/void CCZIReader::EnumSubset(const IDimCoordinate* planeCoordinate, const IntRect* roi, bool onlyLayer0, const std::function& funcEnum) { this->ThrowIfNotOperational(); - CziReaderCommon::EnumSubset(this, planeCoordinate, roi, onlyLayer0, funcEnum); - /* - // TODO: - // Ok... for a first tentative, experimental and quick-n-dirty implementation, simply - // walk through all the subblocks. We surely want to have something more elaborated - // here. - this->EnumerateSubBlocks( - [&](int index, const SubBlockInfo& info)->bool - { - // TODO: we only deal with layer 0 currently... or, more precisely, we do not take "zoom" into account at all - // -> well... added that boolean "onlyLayer0" - is this sufficient...? - if (onlyLayer0 == false || (info.physicalSize.w == info.logicalRect.w && info.physicalSize.h == info.logicalRect.h)) - { - if (planeCoordinate == nullptr || CziUtils::CompareCoordinate(planeCoordinate, &info.coordinate) == true) - { - if (roi == nullptr || Utilities::DoIntersect(*roi, info.logicalRect)) - { - bool b = funcEnum(index, info); - return b; - } - } - } - - return true; - }); - */ } /*virtual*/std::shared_ptr CCZIReader::ReadSubBlock(int index) @@ -182,41 +147,6 @@ CCZIReader::CCZIReader() : isOperational(false) { this->ThrowIfNotOperational(); return CziReaderCommon::TryGetSubBlockInfoOfArbitrarySubBlockInChannel(this, channelIndex, info); - - // TODO: we should be able to gather this information when constructing the subblock-list - // for the time being... just walk through the whole list - // - //bool foundASubBlock = false; - //SubBlockStatistics s = this->subBlkDir.GetStatistics(); - //if (!s.dimBounds.IsValid(DimensionIndex::C)) - //{ - // // in this case -> just take the first subblock... - // this->EnumerateSubBlocks( - // [&](int index, const SubBlockInfo& sbinfo)->bool - // { - // info = sbinfo; - // foundASubBlock = true; - // return false; - // }); - //} - //else - //{ - // this->EnumerateSubBlocks( - // [&](int index, const SubBlockInfo& sbinfo)->bool - // { - // int c; - // if (sbinfo.coordinate.TryGetPosition(DimensionIndex::C, &c) == true && c == channelIndex) - // { - // info = sbinfo; - // foundASubBlock = true; - // return false; - // } - - // return true; - // }); - //} - - //return foundASubBlock; } /*virtual*/bool CCZIReader::TryGetSubBlockInfo(int index, SubBlockInfo* info) const @@ -230,13 +160,6 @@ CCZIReader::CCZIReader() : isOperational(false) if (info != nullptr) { *info = CziReaderCommon::ConvertToSubBlockInfo(entry); - /* info->compressionModeRaw = entry.Compression; - info->pixelType = CziUtils::PixelTypeFromInt(entry.PixelType); - info->coordinate = entry.coordinate; - info->logicalRect = IntRect{ entry.x,entry.y,entry.width,entry.height }; - info->physicalSize = IntSize{ static_cast(entry.storedWidth), static_cast(entry.storedHeight) }; - info->mIndex = entry.mIndex; - info->pyramidType = CziUtils::PyramidTypeFromByte(entry.pyramid_type_from_spare);*/ } return true; @@ -280,27 +203,7 @@ CCZIReader::CCZIReader() : isOperational(false) /*virtual*/void CCZIReader::EnumerateSubset(const char* contentFileType, const char* name, const std::function& funcEnum) { this->ThrowIfNotOperational(); - /* libCZI::AttachmentInfo ai; - ai.contentFileType[sizeof(ai.contentFileType) - 1] = '\0'; - this->attachmentDir.EnumAttachments( - [&](int index, const CCziAttachmentsDirectory::AttachmentEntry& ae) - { - if (contentFileType == nullptr || strcmp(contentFileType, ae.ContentFileType) == 0) - { - if (name == nullptr || strcmp(name, ae.Name) == 0) - { - ai.contentGuid = ae.ContentGuid; - memcpy(ai.contentFileType, ae.ContentFileType, sizeof(ae.ContentFileType)); - ai.name = ae.Name; - bool b = funcEnum(index, ai); - return b; - } - } - - return true; - });*/ CziReaderCommon::EnumerateSubset( - //this->attachmentDir.EnumAttachments, std::bind(&CCziAttachmentsDirectory::EnumAttachments, &this->attachmentDir, std::placeholders::_1), contentFileType, name, diff --git a/Src/libCZI/CziReaderCommon.h b/Src/libCZI/CziReaderCommon.h index 73e0b78f..c750a778 100644 --- a/Src/libCZI/CziReaderCommon.h +++ b/Src/libCZI/CziReaderCommon.h @@ -4,6 +4,7 @@ #include "CziAttachment.h" #include +/// Here we gather some common functionality that is used by both the CziReader and the CziReaderWriter classes. class CziReaderCommon { public: From 8d270fb335896c99ccb0377f9887bcc7e1ceb50c Mon Sep 17 00:00:00 2001 From: ptahmose Date: Sun, 11 Feb 2024 13:40:02 +0100 Subject: [PATCH 09/13] add unittests for newly implemented methods of CziReaderWriter --- Src/libCZI/CziReaderWriter.cpp | 57 +++++--------- Src/libCZI_UnitTests/test_readerwriter.cpp | 86 ++++++++++++++++++++-- 2 files changed, 100 insertions(+), 43 deletions(-) diff --git a/Src/libCZI/CziReaderWriter.cpp b/Src/libCZI/CziReaderWriter.cpp index 89f5925f..435ee737 100644 --- a/Src/libCZI/CziReaderWriter.cpp +++ b/Src/libCZI/CziReaderWriter.cpp @@ -269,14 +269,14 @@ void CCziReaderWriter::Finish() sbBlkDirWriteInfo.segmentPosForNewSegment = this->nextSegmentInfo.GetNextSegmentPos(); sbBlkDirWriteInfo.enumEntriesFunc = [&](const std::function& f)->void - { - this->sbBlkDirectory.EnumEntries( - [&](size_t index, const CCziSubBlockDirectoryBase::SubBlkEntry& e)->bool - { - f(index, e); - return true; - }); - }; + { + this->sbBlkDirectory.EnumEntries( + [&](size_t index, const CCziSubBlockDirectoryBase::SubBlkEntry& e)->bool + { + f(index, e); + return true; + }); + }; sbBlkDirWriteInfo.writeFunc = std::bind(&CCziReaderWriter::WriteToOutputStream, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4, placeholders::_5); const auto posAndSize = CWriterUtils::WriteSubBlkDirectory(sbBlkDirWriteInfo); this->subBlockDirectorySegment.SetPositionAndAllocatedSize(get<0>(posAndSize), get<1>(posAndSize), false); @@ -307,14 +307,14 @@ void CCziReaderWriter::Finish() attchmntDirWriteInfo.segmentPosForNewSegment = this->nextSegmentInfo.GetNextSegmentPos(); attchmntDirWriteInfo.entryCnt = this->attachmentDirectory.GetEntryCnt(); attchmntDirWriteInfo.enumEntriesFunc = [&](const std::function& f)->void - { - this->attachmentDirectory.EnumEntries( - [&](size_t index, const CCziAttachmentsDirectoryBase::AttachmentEntry& e)->bool - { - f(index, e); - return true; - }); - }; + { + this->attachmentDirectory.EnumEntries( + [&](size_t index, const CCziAttachmentsDirectoryBase::AttachmentEntry& e)->bool + { + f(index, e); + return true; + }); + }; attchmntDirWriteInfo.writeFunc = std::bind(&CCziReaderWriter::WriteToOutputStream, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4, placeholders::_5); const auto posAndSize = CWriterUtils::WriteAttachmentDirectory(attchmntDirWriteInfo); this->attachmentDirectorySegment.SetPositionAndAllocatedSize(get<0>(posAndSize), get<1>(posAndSize), false); @@ -483,7 +483,7 @@ void CCziReaderWriter::DetermineNextSubBlockOffset() lastSegmentPos = sbBlkEntry.FilePosition; } - return true; + return true; }); this->attachmentDirectory.EnumEntries( @@ -494,7 +494,7 @@ void CCziReaderWriter::DetermineNextSubBlockOffset() lastSegmentPos = attEntry.FilePosition; } - return true; + return true; }); if (this->hdrSegmentData.GetIsSubBlockDirectoryPositionValid()) @@ -642,15 +642,6 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, this->sbBlkDirectory.EnumEntries( [&](int index, const CCziSubBlockDirectory::SubBlkEntry& entry)->bool { - /*SubBlockInfo info; - info.compressionModeRaw = entry.Compression; - info.pixelType = CziUtils::PixelTypeFromInt(entry.PixelType); - info.coordinate = entry.coordinate; - info.logicalRect = IntRect{ entry.x,entry.y,entry.width,entry.height }; - info.physicalSize = IntSize{ (std::uint32_t)entry.storedWidth, (std::uint32_t)entry.storedHeight }; - info.mIndex = entry.mIndex; - info.pyramidType = CziUtils::PyramidTypeFromByte(entry.pyramid_type_from_spare); - return funcEnum(index, info);*/ return funcEnum(index, CziReaderCommon::ConvertToSubBlockInfo(entry)); }); } @@ -698,13 +689,6 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, if (info != nullptr) { *info = CziReaderCommon::ConvertToSubBlockInfo(entry); - /*info->compressionModeRaw = entry.Compression; - info->pixelType = CziUtils::PixelTypeFromInt(entry.PixelType); - info->coordinate = entry.coordinate; - info->logicalRect = IntRect{ entry.x,entry.y,entry.width,entry.height }; - info->physicalSize = IntSize{ static_cast(entry.storedWidth), static_cast(entry.storedHeight) }; - info->mIndex = entry.mIndex; - info->pyramidType = CziUtils::PyramidTypeFromByte(entry.pyramid_type_from_spare);*/ } return true; @@ -714,7 +698,6 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, { this->ThrowIfNotOperational(); return CziReaderCommon::TryGetSubBlockInfoOfArbitrarySubBlockInChannel(this, channelIndex, info); - // throw std::runtime_error("Not Implemented"); } /*virtual*/libCZI::SubBlockStatistics CCziReaderWriter::GetStatistics() @@ -750,8 +733,8 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, //throw std::runtime_error("Not Implemented"); CziReaderCommon::EnumerateSubset( std::bind(&CReaderWriterCziAttachmentsDirectory::EnumEntries, &this->attachmentDirectory, std::placeholders::_1), - contentFileType, - name, + contentFileType, + name, funcEnum); } diff --git a/Src/libCZI_UnitTests/test_readerwriter.cpp b/Src/libCZI_UnitTests/test_readerwriter.cpp index 014fba4d..0e19c4b4 100644 --- a/Src/libCZI_UnitTests/test_readerwriter.cpp +++ b/Src/libCZI_UnitTests/test_readerwriter.cpp @@ -1393,12 +1393,12 @@ TEST(CziReaderWriter, TestEnumerateSubBlocks) { auto testCzi = CreateTestCzi(); - auto input_output_stream = make_shared(get<0>(testCzi).get(), get<1>(testCzi)); - auto rw = CreateCZIReaderWriter(); - rw->Create(input_output_stream); + const auto input_output_stream = make_shared(get<0>(testCzi).get(), get<1>(testCzi)); + const auto reader_writer = CreateCZIReaderWriter(); + reader_writer->Create(input_output_stream); vector indices; - rw->EnumerateSubBlocks( + reader_writer->EnumerateSubBlocks( [&](int index, const SubBlockInfo& info)->bool { indices.push_back(index); @@ -1423,7 +1423,7 @@ TEST(CziReaderWriter, TestEnumerateSubBlocks) indices.clear(); const auto query_rect = IntRect{ 8,0,8,1 }; - rw->EnumSubset(nullptr, &query_rect, true, + reader_writer->EnumSubset(nullptr, &query_rect, true, [&](int index, const SubBlockInfo& info)->bool { indices.push_back(index); @@ -1440,9 +1440,83 @@ TEST(CziReaderWriter, TestEnumerateSubBlocks) // check that the subblock is within the query rectangle SubBlockInfo sub_block_info; - bool b = rw->TryGetSubBlockInfo(*it, &sub_block_info); + bool b = reader_writer->TryGetSubBlockInfo(*it, &sub_block_info); ASSERT_TRUE(b); b = query_rect.IntersectsWith(sub_block_info.logicalRect); ASSERT_TRUE(b); } } + +TEST(CziReaderWriter, AttachmentEnumerateSubset) +{ + auto testCzi = CreateTestCzi2(); + + const auto input_output_stream = make_shared(get<0>(testCzi).get(), get<1>(testCzi)); + const auto reader_writer = CreateCZIReaderWriter(); + reader_writer->Create(input_output_stream); + + // compare the results of EnumerateAttachments and EnumerateSubset (where no subset is specified), + // this should give the same result + vector indices_from_enumerate; + reader_writer->EnumerateAttachments( + [&](int index, const AttachmentInfo& info)->bool + { + indices_from_enumerate.push_back(index); + return true; + }); + + vector indices_from_enumerate_subset; + reader_writer->EnumerateSubset( + nullptr, nullptr, + [&](int index, const AttachmentInfo& info)->bool + { + indices_from_enumerate_subset.push_back(index); + return true; + }); + + // The matcher will check if 'actual' contains all the elements of 'expected', ignoring order + EXPECT_THAT(indices_from_enumerate, ::testing::UnorderedElementsAreArray(indices_from_enumerate_subset)); + + // Now use a condition to filter the attachments (only those with name "ATTACHMENT1" should be found). + // Note that our CZI-document only has one attachment with this name, so the result should be the same as before. + indices_from_enumerate_subset.clear(); + reader_writer->EnumerateSubset( + nullptr, "ATTACHMENT1", + [&](int index, const AttachmentInfo& info)->bool + { + indices_from_enumerate_subset.push_back(index); + return true; + }); + + EXPECT_THAT(indices_from_enumerate, ::testing::UnorderedElementsAreArray(indices_from_enumerate_subset)); + + indices_from_enumerate_subset.clear(); + + // Now use a condition which is not met by any attachment (so the result should be an empty vector). + reader_writer->EnumerateSubset( + nullptr, "XXXXXXXXXXX", + [&](int index, const AttachmentInfo& info)->bool + { + indices_from_enumerate_subset.push_back(index); + return true; + }); + + EXPECT_EQ(indices_from_enumerate_subset.size(), 0); +} + +TEST(CziReaderWriter, TryGetSubBlockInfoOfArbitrarySubBlockInChannel) +{ + auto testCzi = CreateTestCzi2(); + + const auto input_output_stream = make_shared(get<0>(testCzi).get(), get<1>(testCzi)); + const auto reader_writer = CreateCZIReaderWriter(); + reader_writer->Create(input_output_stream); + + SubBlockInfo sub_block_info; + bool b = reader_writer->TryGetSubBlockInfoOfArbitrarySubBlockInChannel(0, sub_block_info); + ASSERT_TRUE(b); + EXPECT_EQ(sub_block_info.pixelType, PixelType::Gray8); + + b = reader_writer->TryGetSubBlockInfoOfArbitrarySubBlockInChannel(1, sub_block_info); + EXPECT_FALSE(b); +} From 3b7c66f2b79f833d435917d1ddf3b0fc730c5d76 Mon Sep 17 00:00:00 2001 From: ptahmose Date: Sun, 11 Feb 2024 13:50:19 +0100 Subject: [PATCH 10/13] cosmetic --- Src/libCZI/CziReaderCommon.cpp | 8 +++++--- Src/libCZI/CziReaderCommon.h | 4 ++++ Src/libCZI/libCZI_ReadWrite.h | 4 +++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Src/libCZI/CziReaderCommon.cpp b/Src/libCZI/CziReaderCommon.cpp index 24030e72..64683e0c 100644 --- a/Src/libCZI/CziReaderCommon.cpp +++ b/Src/libCZI/CziReaderCommon.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2024 Carl Zeiss Microscopy GmbH +// +// SPDX-License-Identifier: LGPL-3.0-or-later + #include "CziReaderCommon.h" #include "CziUtils.h" @@ -19,15 +23,13 @@ using namespace libCZI; repository->EnumerateSubBlocks( [&](int index, const SubBlockInfo& info)->bool { - // TODO: we only deal with layer 0 currently... or, more precisely, we do not take "zoom" into account at all - // -> well... added that boolean "onlyLayer0" - is this sufficient...? if (onlyLayer0 == false || (info.physicalSize.w == info.logicalRect.w && info.physicalSize.h == info.logicalRect.h)) { if (planeCoordinate == nullptr || CziUtils::CompareCoordinate(planeCoordinate, &info.coordinate) == true) { if (roi == nullptr || Utilities::DoIntersect(*roi, info.logicalRect)) { - bool b = funcEnum(index, info); + const bool b = funcEnum(index, info); return b; } } diff --git a/Src/libCZI/CziReaderCommon.h b/Src/libCZI/CziReaderCommon.h index c750a778..ab43da7a 100644 --- a/Src/libCZI/CziReaderCommon.h +++ b/Src/libCZI/CziReaderCommon.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2024 Carl Zeiss Microscopy GmbH +// +// SPDX-License-Identifier: LGPL-3.0-or-later + #pragma once #include "libCZI.h" diff --git a/Src/libCZI/libCZI_ReadWrite.h b/Src/libCZI/libCZI_ReadWrite.h index 427692c8..d2a19757 100644 --- a/Src/libCZI/libCZI_ReadWrite.h +++ b/Src/libCZI/libCZI_ReadWrite.h @@ -33,7 +33,9 @@ namespace libCZI /// - The indices (or "keys") for a subblock/attachment do not change during the lifetime of the object (even if deleting some). /// - Contrary to ICziWriter, this object does not attempt to verify the consistency of the coordinates - which is due the fact /// that we aim at allowing arbitrary modifications. We do not require to specify in advance the number of dimensions or the bounds. - /// - The information returned by ISubBlockRepository::GetStatistics is valid (taking into consideration the current state). + /// - The information returned by ISubBlockRepository::GetStatistics is valid (taking into consideration the current state). + /// - When enumerating subblocks/attachments, mutations (i.e. adding/removing items) of the respective subblock/attachment-collection are not allowed + /// and result in undefined behavior. class LIBCZI_API ICziReaderWriter : public ISubBlockRepository, public IAttachmentRepository { public: From 9e2843c0993c9c7f9cd7978da45f55429d972b6f Mon Sep 17 00:00:00 2001 From: ptahmose Date: Sun, 11 Feb 2024 13:57:02 +0100 Subject: [PATCH 11/13] cosmetic --- Src/libCZI/CziReaderCommon.cpp | 22 +++++++++++----------- Src/libCZI/CziReaderWriter.cpp | 1 - 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Src/libCZI/CziReaderCommon.cpp b/Src/libCZI/CziReaderCommon.cpp index 64683e0c..cff8ea1d 100644 --- a/Src/libCZI/CziReaderCommon.cpp +++ b/Src/libCZI/CziReaderCommon.cpp @@ -45,7 +45,7 @@ using namespace libCZI; libCZI::SubBlockInfo& info) { bool foundASubBlock = false; - SubBlockStatistics s = repository->GetStatistics();// this->subBlkDir.GetStatistics(); + SubBlockStatistics s = repository->GetStatistics(); if (!s.dimBounds.IsValid(DimensionIndex::C)) { // in this case -> just take the first subblock... @@ -88,19 +88,19 @@ using namespace libCZI; func( [&](int index, const CCziAttachmentsDirectoryBase::AttachmentEntry& ae) { - if (contentFileType == nullptr || strcmp(contentFileType, ae.ContentFileType) == 0) - { - if (name == nullptr || strcmp(name, ae.Name) == 0) + if (contentFileType == nullptr || strcmp(contentFileType, ae.ContentFileType) == 0) { - ai.contentGuid = ae.ContentGuid; - memcpy(ai.contentFileType, ae.ContentFileType, sizeof(ae.ContentFileType)); - ai.name = ae.Name; - bool b = funcEnum(index, ai); - return b; + if (name == nullptr || strcmp(name, ae.Name) == 0) + { + ai.contentGuid = ae.ContentGuid; + memcpy(ai.contentFileType, ae.ContentFileType, sizeof(ae.ContentFileType)); + ai.name = ae.Name; + bool b = funcEnum(index, ai); + return b; + } } - } - return true; + return true; }); } diff --git a/Src/libCZI/CziReaderWriter.cpp b/Src/libCZI/CziReaderWriter.cpp index 435ee737..df41d723 100644 --- a/Src/libCZI/CziReaderWriter.cpp +++ b/Src/libCZI/CziReaderWriter.cpp @@ -730,7 +730,6 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, /*virtual*/void CCziReaderWriter::EnumerateSubset(const char* contentFileType, const char* name, const std::function& funcEnum) { this->ThrowIfNotOperational(); - //throw std::runtime_error("Not Implemented"); CziReaderCommon::EnumerateSubset( std::bind(&CReaderWriterCziAttachmentsDirectory::EnumEntries, &this->attachmentDirectory, std::placeholders::_1), contentFileType, From cb591c2ba6786ff6fc0470f4009ef84c3e2ede9f Mon Sep 17 00:00:00 2001 From: ptahmose Date: Sun, 11 Feb 2024 14:08:30 +0100 Subject: [PATCH 12/13] linter --- Src/libCZI/CziReaderCommon.cpp | 8 ++++---- Src/libCZI_UnitTests/test_readerwriter.cpp | 9 ++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Src/libCZI/CziReaderCommon.cpp b/Src/libCZI/CziReaderCommon.cpp index cff8ea1d..91ee3141 100644 --- a/Src/libCZI/CziReaderCommon.cpp +++ b/Src/libCZI/CziReaderCommon.cpp @@ -78,10 +78,10 @@ using namespace libCZI; } /*static*/void CziReaderCommon::EnumerateSubset( - const std::function&)>& func, - const char* contentFileType, - const char* name, - const std::function& funcEnum) + const std::function&)>& func, + const char* contentFileType, + const char* name, + const std::function& funcEnum) { libCZI::AttachmentInfo ai; ai.contentFileType[sizeof(ai.contentFileType) - 1] = '\0'; diff --git a/Src/libCZI_UnitTests/test_readerwriter.cpp b/Src/libCZI_UnitTests/test_readerwriter.cpp index 0e19c4b4..cf082652 100644 --- a/Src/libCZI_UnitTests/test_readerwriter.cpp +++ b/Src/libCZI_UnitTests/test_readerwriter.cpp @@ -1422,9 +1422,12 @@ TEST(CziReaderWriter, TestEnumerateSubBlocks) ASSERT_TRUE(allNumbersPresent) << "Not all numbers from 0 to 49 are present in the vector."; indices.clear(); - const auto query_rect = IntRect{ 8,0,8,1 }; - reader_writer->EnumSubset(nullptr, &query_rect, true, - [&](int index, const SubBlockInfo& info)->bool + constexpr auto query_rect = IntRect{ 8,0,8,1 }; + reader_writer->EnumSubset( + nullptr, + &query_rect, + true, + [&](int index, const SubBlockInfo& info)->bool { indices.push_back(index); return true; From 15e71987890214a8ba3df438c79a8a8c9804b13f Mon Sep 17 00:00:00 2001 From: ptahmose Date: Sun, 11 Feb 2024 21:59:42 +0100 Subject: [PATCH 13/13] formatting... --- Src/libCZI/CziReaderWriter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Src/libCZI/CziReaderWriter.cpp b/Src/libCZI/CziReaderWriter.cpp index df41d723..c8241179 100644 --- a/Src/libCZI/CziReaderWriter.cpp +++ b/Src/libCZI/CziReaderWriter.cpp @@ -273,8 +273,8 @@ void CCziReaderWriter::Finish() this->sbBlkDirectory.EnumEntries( [&](size_t index, const CCziSubBlockDirectoryBase::SubBlkEntry& e)->bool { - f(index, e); - return true; + f(index, e); + return true; }); }; sbBlkDirWriteInfo.writeFunc = std::bind(&CCziReaderWriter::WriteToOutputStream, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4, placeholders::_5); @@ -311,8 +311,8 @@ void CCziReaderWriter::Finish() this->attachmentDirectory.EnumEntries( [&](size_t index, const CCziAttachmentsDirectoryBase::AttachmentEntry& e)->bool { - f(index, e); - return true; + f(index, e); + return true; }); }; attchmntDirWriteInfo.writeFunc = std::bind(&CCziReaderWriter::WriteToOutputStream, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4, placeholders::_5);