diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fc36912..96073bb3 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.51.1 + VERSION 0.52.0 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/CZIReader.cpp b/Src/libCZI/CZIReader.cpp index 617190a5..271c43f9 100644 --- a/Src/libCZI/CZIReader.cpp +++ b/Src/libCZI/CZIReader.cpp @@ -113,6 +113,7 @@ CCZIReader::~CCZIReader() 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); }); } @@ -130,6 +131,7 @@ CCZIReader::~CCZIReader() 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); info.filePosition = entry.FilePosition; return funcEnum(index, info); }); @@ -232,6 +234,7 @@ CCZIReader::~CCZIReader() 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; @@ -266,7 +269,7 @@ CCZIReader::~CCZIReader() }); } -/*virtual*/void CCZIReader::EnumerateSubset(const char* contentFileType, const char* name, const std::function& funcEnum) +/*virtual*/void CCZIReader::EnumerateSubset(const char* contentFileType, const char* name, const std::function& funcEnum) { this->ThrowIfNotOperational(); libCZI::AttachmentInfo ai; @@ -315,6 +318,7 @@ std::shared_ptr CCZIReader::ReadSubBlock(const CCziSubBlockDirectory: info.mIndex = subBlkData.mIndex; info.logicalRect = subBlkData.logicalRect; info.physicalSize = subBlkData.physicalSize; + info.pyramidType = CziUtils::PyramidTypeFromByte(subBlkData.spare[0]); return std::make_shared(info, subBlkData, free); } diff --git a/Src/libCZI/CZIReader.h b/Src/libCZI/CZIReader.h index 3a8b4565..135941ed 100644 --- a/Src/libCZI/CZIReader.h +++ b/Src/libCZI/CZIReader.h @@ -43,7 +43,7 @@ class CCZIReader : public libCZI::ICZIReader, public std::enable_shared_from_thi void Close() override; // interface IAttachmentRepository - void EnumerateAttachments(const std::function& funcEnum) override; + void EnumerateAttachments(const std::function& funcEnum) override; void EnumerateSubset(const char* contentFileType, const char* name, const std::function& funcEnum) override; std::shared_ptr ReadAttachment(int index) override; diff --git a/Src/libCZI/CziParse.cpp b/Src/libCZI/CziParse.cpp index 46011c77..20ba363e 100644 --- a/Src/libCZI/CziParse.cpp +++ b/Src/libCZI/CziParse.cpp @@ -281,6 +281,7 @@ using namespace libCZI; sbd.compression = subBlckSegment.data.entryDV.Compression; sbd.pixelType = subBlckSegment.data.entryDV.PixelType; sbd.mIndex = (std::numeric_limits::max)(); + memcpy(sbd.spare, subBlckSegment.data.entryDV._spare, sizeof(sbd.spare)); if (subBlckSegment.data.entryDV.DimensionCount > MAXDIMENSIONS) { @@ -616,6 +617,7 @@ using namespace libCZI; entry.FilePosition = subBlkDirDV->FilePosition; entry.PixelType = subBlkDirDV->PixelType; entry.Compression = subBlkDirDV->Compression; + entry.pyramid_type_from_spare = subBlkDirDV->_spare[0]; addFunc(entry); } diff --git a/Src/libCZI/CziParse.h b/Src/libCZI/CziParse.h index 1a900b33..918f38c9 100644 --- a/Src/libCZI/CziParse.h +++ b/Src/libCZI/CziParse.h @@ -160,6 +160,7 @@ class CCZIParse libCZI::IntRect logicalRect; libCZI::IntSize physicalSize; int mIndex; // if not present, then this is int::max + std::uint8_t spare[6]; }; static SubBlockData ReadSubBlock(libCZI::IStream* str, std::uint64_t offset, const SubBlockStorageAllocate& allocateInfo); diff --git a/Src/libCZI/CziReaderWriter.cpp b/Src/libCZI/CziReaderWriter.cpp index 302e1b20..fecba4cf 100644 --- a/Src/libCZI/CziReaderWriter.cpp +++ b/Src/libCZI/CziReaderWriter.cpp @@ -677,6 +677,7 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, info.mIndex = subBlkData.mIndex; info.logicalRect = subBlkData.logicalRect; info.physicalSize = subBlkData.physicalSize; + info.pyramidType = CziUtils::PyramidTypeFromByte(subBlkData.spare[0]); return std::make_shared(info, subBlkData, free); } @@ -698,6 +699,7 @@ void CCziReaderWriter::WriteToOutputStream(std::uint64_t offset, const void* pv, 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; diff --git a/Src/libCZI/CziReaderWriter.h b/Src/libCZI/CziReaderWriter.h index 2b11f2d2..a0401cfd 100644 --- a/Src/libCZI/CziReaderWriter.h +++ b/Src/libCZI/CziReaderWriter.h @@ -50,7 +50,7 @@ class CCziReaderWriter : public libCZI::ICziReaderWriter // interface IAttachmentRepository void EnumerateAttachments(const std::function& funcEnum) override; - void EnumerateSubset(const char* contentFileType, const char* name, const std::function& funcEnum) override; + void EnumerateSubset(const char* contentFileType, const char* name, const std::function& funcEnum) override; std::shared_ptr ReadAttachment(int index) override; private: diff --git a/Src/libCZI/CziStructs.h b/Src/libCZI/CziStructs.h index 286e32c4..33778799 100644 --- a/Src/libCZI/CziStructs.h +++ b/Src/libCZI/CziStructs.h @@ -179,7 +179,16 @@ struct PACKED SubBlockDirectoryEntryDV std::int64_t FilePosition; std::int32_t FilePart; std::int32_t Compression; - unsigned char _spare[6]; + + /// _spare[0] seems to contain information about the "pyramid-type", where valid values are + /// + /// 0: None + /// 1: SingleSubblock + /// 2: MultiSubblock + /// + /// The significance and importance of this field is unclear, and it seems of questionable use. It is + /// considered legacy and should not be used. + std::uint8_t _spare[6]; std::int32_t DimensionCount; // max. allocation for ease of use (valid size = 32 + EntryCount * 20) diff --git a/Src/libCZI/CziSubBlockDirectory.h b/Src/libCZI/CziSubBlockDirectory.h index 6605338d..04bd0c26 100644 --- a/Src/libCZI/CziSubBlockDirectory.h +++ b/Src/libCZI/CziSubBlockDirectory.h @@ -27,6 +27,7 @@ class CCziSubBlockDirectoryBase int PixelType; std::uint64_t FilePosition; int Compression; + std::uint8_t pyramid_type_from_spare; ///< The field "pyramid-type" (from spare-bytes of the subblock-directory-entry) bool IsMIndexValid() const { @@ -41,6 +42,7 @@ class CCziSubBlockDirectoryBase void Invalidate() { this->mIndex = this->x = this->y = this->width = this->height = this->storedWidth = this->storedHeight = (std::numeric_limits::min)(); + this->pyramid_type_from_spare = 0; } }; diff --git a/Src/libCZI/CziUtils.cpp b/Src/libCZI/CziUtils.cpp index 8d2bc029..4a4ac2d5 100644 --- a/Src/libCZI/CziUtils.cpp +++ b/Src/libCZI/CziUtils.cpp @@ -28,6 +28,30 @@ using namespace libCZI; return PixelType::Invalid; } +/*static*/libCZI::SubBlockPyramidType CziUtils::PyramidTypeFromByte(std::uint8_t byte) +{ + switch (byte) + { + case 0: return SubBlockPyramidType::None; + case 1: return SubBlockPyramidType::SingleSubBlock; + case 2: return SubBlockPyramidType::MultiSubBlock; + } + + return SubBlockPyramidType::Invalid; +} + +/*static*/std::uint8_t CziUtils::ByteFromPyramidType(libCZI::SubBlockPyramidType pyramid_type) +{ + switch (pyramid_type) + { + case SubBlockPyramidType::None: return 0; + case SubBlockPyramidType::SingleSubBlock: return 1; + case SubBlockPyramidType::MultiSubBlock: return 2; + } + + return 0; +} + /*static*/int CziUtils::IntFromPixelType(libCZI::PixelType p) { switch (p) diff --git a/Src/libCZI/CziUtils.h b/Src/libCZI/CziUtils.h index 9d90a0c7..d072e7f2 100644 --- a/Src/libCZI/CziUtils.h +++ b/Src/libCZI/CziUtils.h @@ -20,6 +20,8 @@ class CziUtils { public: static libCZI::PixelType PixelTypeFromInt(int i); + static libCZI::SubBlockPyramidType PyramidTypeFromByte(std::uint8_t byte); + static std::uint8_t ByteFromPyramidType(libCZI::SubBlockPyramidType pyramid_type); static int IntFromPixelType(libCZI::PixelType); static libCZI::CompressionMode CompressionModeFromInt(int i); static std::int32_t CompressionModeToInt(libCZI::CompressionMode m); diff --git a/Src/libCZI/CziWriter.cpp b/Src/libCZI/CziWriter.cpp index 779f8a08..2c4813a9 100644 --- a/Src/libCZI/CziWriter.cpp +++ b/Src/libCZI/CziWriter.cpp @@ -31,21 +31,21 @@ void libCZI::ICziWriter::SyncAddSubBlock(const libCZI::AddSubBlockInfoMemPtr& ad AddSubBlockInfo addSbInfo(addSbBlkInfo); addSbInfo.sizeData = addSbBlkInfo.dataSize; addSbInfo.getData = [&](int callCnt, size_t offset, const void*& ptr, size_t& size)->bool - { - return SetIfCallCountZero(callCnt, addSbBlkInfo.ptrData, addSbBlkInfo.dataSize, ptr, size); - }; + { + return SetIfCallCountZero(callCnt, addSbBlkInfo.ptrData, addSbBlkInfo.dataSize, ptr, size); + }; addSbInfo.sizeAttachment = addSbBlkInfo.sbBlkAttachmentSize; addSbInfo.getAttachment = [&](int callCnt, size_t offset, const void*& ptr, size_t& size)->bool - { - return SetIfCallCountZero(callCnt, addSbBlkInfo.ptrSbBlkAttachment, addSbBlkInfo.sbBlkAttachmentSize, ptr, size); - }; + { + return SetIfCallCountZero(callCnt, addSbBlkInfo.ptrSbBlkAttachment, addSbBlkInfo.sbBlkAttachmentSize, ptr, size); + }; addSbInfo.sizeMetadata = addSbBlkInfo.sbBlkMetadataSize; addSbInfo.getMetaData = [&](int callCnt, size_t offset, const void*& ptr, size_t& size)->bool - { - return SetIfCallCountZero(callCnt, addSbBlkInfo.ptrSbBlkMetadata, addSbBlkInfo.sbBlkMetadataSize, ptr, size); - }; + { + return SetIfCallCountZero(callCnt, addSbBlkInfo.ptrSbBlkMetadata, addSbBlkInfo.sbBlkMetadataSize, ptr, size); + }; return this->SyncAddSubBlock(addSbInfo); } @@ -58,28 +58,28 @@ void libCZI::ICziWriter::SyncAddSubBlock(const libCZI::AddSubBlockInfoLinewiseBi addSbInfo.sizeData = addSbInfoLinewise.physicalHeight * stride; auto linesCnt = addSbInfoLinewise.physicalHeight; addSbInfo.getData = [&](int callCnt, size_t offset, const void*& ptr, size_t& size)->bool - { - if (callCnt < linesCnt) { - ptr = addSbInfoLinewise.getBitmapLine(callCnt); - size = stride; - return true; - } + if (callCnt < linesCnt) + { + ptr = addSbInfoLinewise.getBitmapLine(callCnt); + size = stride; + return true; + } - return false; - }; + return false; + }; addSbInfo.sizeAttachment = addSbInfoLinewise.sbBlkAttachmentSize; addSbInfo.getAttachment = [&](int callCnt, size_t offset, const void*& ptr, size_t& size)->bool - { - return SetIfCallCountZero(callCnt, addSbInfoLinewise.ptrSbBlkAttachment, addSbInfoLinewise.sbBlkAttachmentSize, ptr, size); - }; + { + return SetIfCallCountZero(callCnt, addSbInfoLinewise.ptrSbBlkAttachment, addSbInfoLinewise.sbBlkAttachmentSize, ptr, size); + }; addSbInfo.sizeMetadata = addSbInfoLinewise.sbBlkMetadataSize; addSbInfo.getMetaData = [&](int callCnt, size_t offset, const void*& ptr, size_t& size)->bool - { - return SetIfCallCountZero(callCnt, addSbInfoLinewise.ptrSbBlkMetadata, addSbInfoLinewise.sbBlkMetadataSize, ptr, size); - }; + { + return SetIfCallCountZero(callCnt, addSbInfoLinewise.ptrSbBlkMetadata, addSbInfoLinewise.sbBlkMetadataSize, ptr, size); + }; return this->SyncAddSubBlock(addSbInfo); } @@ -90,28 +90,28 @@ void libCZI::ICziWriter::SyncAddSubBlock(const AddSubBlockInfoStridedBitmap& add addSbInfo.sizeData = addSbBlkInfoStrideBitmap.physicalHeight * (addSbBlkInfoStrideBitmap.physicalWidth * (size_t)CziUtils::GetBytesPerPel(addSbBlkInfoStrideBitmap.PixelType)); addSbInfo.getData = [&](int callCnt, size_t offset, const void*& ptr, size_t& size)->bool - { - if (callCnt < addSbBlkInfoStrideBitmap.physicalHeight) { - ptr = static_cast(addSbBlkInfoStrideBitmap.ptrBitmap) + callCnt * (size_t)addSbBlkInfoStrideBitmap.strideBitmap; - size = addSbBlkInfoStrideBitmap.physicalWidth * (size_t)CziUtils::GetBytesPerPel(addSbBlkInfoStrideBitmap.PixelType); - return true; - } + if (callCnt < addSbBlkInfoStrideBitmap.physicalHeight) + { + ptr = static_cast(addSbBlkInfoStrideBitmap.ptrBitmap) + callCnt * (size_t)addSbBlkInfoStrideBitmap.strideBitmap; + size = addSbBlkInfoStrideBitmap.physicalWidth * (size_t)CziUtils::GetBytesPerPel(addSbBlkInfoStrideBitmap.PixelType); + return true; + } - return false; - }; + return false; + }; addSbInfo.sizeAttachment = addSbBlkInfoStrideBitmap.sbBlkAttachmentSize; addSbInfo.getAttachment = [&](int callCnt, size_t offset, const void*& ptr, size_t& size)->bool - { - return SetIfCallCountZero(callCnt, addSbBlkInfoStrideBitmap.ptrSbBlkAttachment, addSbBlkInfoStrideBitmap.sbBlkAttachmentSize, ptr, size); - }; + { + return SetIfCallCountZero(callCnt, addSbBlkInfoStrideBitmap.ptrSbBlkAttachment, addSbBlkInfoStrideBitmap.sbBlkAttachmentSize, ptr, size); + }; addSbInfo.sizeMetadata = addSbBlkInfoStrideBitmap.sbBlkMetadataSize; addSbInfo.getMetaData = [&](int callCnt, size_t offset, const void*& ptr, size_t& size)->bool - { - return SetIfCallCountZero(callCnt, addSbBlkInfoStrideBitmap.ptrSbBlkMetadata, addSbBlkInfoStrideBitmap.sbBlkMetadataSize, ptr, size); - }; + { + return SetIfCallCountZero(callCnt, addSbBlkInfoStrideBitmap.ptrSbBlkMetadata, addSbBlkInfoStrideBitmap.sbBlkMetadataSize, ptr, size); + }; return this->SyncAddSubBlock(addSbInfo); } @@ -269,7 +269,8 @@ void libCZI::ICziWriter::SyncAddSubBlock(const AddSubBlockInfoStridedBitmap& add ptr->PixelType = CziUtils::IntFromPixelType(addSbBlkInfo.PixelType); ptr->FilePosition = ptr->FilePart = 0; ptr->Compression = addSbBlkInfo.compressionModeRaw; - memset(ptr->_spare, 0, sizeof(ptr->_spare)); + ptr->_spare[0] = CziUtils::ByteFromPyramidType(addSbBlkInfo.pyramid_type); + memset(ptr->_spare + 1, 0, sizeof(ptr->_spare) - 1); ptr->DimensionCount = CalcCountOfDimensionsEntriesInDirectoryEntryDV(addSbBlkInfo); // first X and Y @@ -331,7 +332,8 @@ void libCZI::ICziWriter::SyncAddSubBlock(const AddSubBlockInfoStridedBitmap& add ptr->FilePosition = entry.FilePosition; ptr->FilePart = 0; ptr->Compression = entry.Compression; - memset(ptr->_spare, 0, sizeof(ptr->_spare)); + ptr->_spare[0] = entry.pyramid_type_from_spare; + memset(ptr->_spare + 1, 0, sizeof(ptr->_spare) - 1); // skipping the pyramid-spare-byte we set above here ptr->DimensionCount = CalcCountOfDimensionsEntriesInDirectoryEntryDV(entry); // first X and Y @@ -829,6 +831,7 @@ void libCZI::ICziWriter::SyncAddSubBlock(const AddSubBlockInfoStridedBitmap& add entry.PixelType = CziUtils::IntFromPixelType(addSbBlkInfo.PixelType); entry.FilePosition = 0; entry.Compression = addSbBlkInfo.compressionModeRaw; + entry.pyramid_type_from_spare = CziUtils::ByteFromPyramidType(addSbBlkInfo.pyramid_type); return entry; } @@ -1176,13 +1179,13 @@ std::tuple CCziWriter::WriteCurrentAttachmentsDir info.segmentPosForNewSegment = this->nextSegmentPos; info.entryCnt = this->attachmentDirectory.GetAttachmentCount(); info.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; + }); + }; info.writeFunc = std::bind(&CCziWriter::WriteToOutputStream, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4, placeholders::_5); auto posAndSize = CWriterUtils::WriteAttachmentDirectory(info); if (get<0>(posAndSize) == info.segmentPosForNewSegment) @@ -1217,13 +1220,13 @@ std::tuple CCziWriter::WriteCurrentSubBlkDirectory info.segmentPosForNewSegment = this->nextSegmentPos; info.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; + }); + }; info.writeFunc = std::bind(&CCziWriter::WriteToOutputStream, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4, placeholders::_5); auto posAndSize = CWriterUtils::WriteSubBlkDirectory(info); if (get<0>(posAndSize) == info.segmentPosForNewSegment) diff --git a/Src/libCZI/libCZI.h b/Src/libCZI/libCZI.h index af719c3c..bf058012 100644 --- a/Src/libCZI/libCZI.h +++ b/Src/libCZI/libCZI.h @@ -231,6 +231,10 @@ namespace libCZI /// The M-index of the sub-block (if available). If not available, it has the value std::numeric_limits::max() or std::numeric_limits::min(). int mIndex; + /// This field indicates the "pyramid-type" of the sub-block. The significance and importance of this field is unclear, and is considered + /// legacy. It is recommended to ignore this field. + SubBlockPyramidType pyramidType; + /// Calculate a zoom-factor from the physical- and logical size. /// \remark /// This calculation not really well-defined. @@ -351,7 +355,7 @@ namespace libCZI /// \return The raw data. virtual std::shared_ptr GetRawData(size_t* ptrSize) = 0; - virtual ~IAttachment() {} + virtual ~IAttachment() = default; /// A helper method used to cast the pointer to a specific type. /// \param [out] ptr The pointer to the data is stored here. @@ -595,7 +599,7 @@ namespace libCZI /// functor is true, the enumeration is continued, otherwise it is stopped. /// The first argument is the index of the attachment and the second is providing /// information about the attachment. - virtual void EnumerateSubset(const char* contentFileType, const char* name, const std::function& funcEnum) = 0; + virtual void EnumerateSubset(const char* contentFileType, const char* name, const std::function& funcEnum) = 0; /// Reads the attachment identified by the specified index. If there is no attachment present (for /// the specified index) then an empty shared_ptr is returned. If a different kind of problem diff --git a/Src/libCZI/libCZI_Pixels.h b/Src/libCZI/libCZI_Pixels.h index fd4ce4f7..48d9b128 100644 --- a/Src/libCZI/libCZI_Pixels.h +++ b/Src/libCZI/libCZI_Pixels.h @@ -134,6 +134,17 @@ namespace libCZI Zstd1 = 6 ///< The data contains a header, followed by a zstd-compressed block. }; + /// This enum is used in the context of a subblock to describe which "type of pyramid" is represented by the subblock. + /// The significance and importance of this enum is not yet fully understood, and seems questionable. It is not recommended + /// to make use of it at this point for any purposes. + enum class SubBlockPyramidType : std::uint8_t + { + Invalid = 0xff, ///< Invalid pyramid type. + None = 0, ///< No pyramid (indicating that the subblock is not a pyramid subblock, but a layer-0 subblock). + SingleSubBlock= 1, ///< The subblock is a pyramid subblock, and it covers a single subblock of the lower layer (or: it is a minification of a single lower-layer-subblock). + MultiSubBlock = 2 ///< The subblock is a pyramid subblock, and it covers multiple subblocks of the lower layer. + }; + /// Information about a locked bitmap - allowing direct access to the image data in memory. struct BitmapLockInfo { diff --git a/Src/libCZI/libCZI_Write.h b/Src/libCZI/libCZI_Write.h index b8184ed5..41c52e1a 100644 --- a/Src/libCZI/libCZI_Write.h +++ b/Src/libCZI/libCZI_Write.h @@ -92,7 +92,7 @@ namespace libCZI /// \return True if space (in bytes) for the metadata is to be reserved, false otherwise. virtual bool TryGetReservedSizeForMetadataSegment(size_t* size) const = 0; - virtual ~ICziWriterInfo() {} + virtual ~ICziWriterInfo() = default; }; /// An implementation of the ICziWriterInfo-interface. @@ -178,18 +178,23 @@ namespace libCZI struct LIBCZI_API AddSubBlockInfoBase { /// Default constructor - AddSubBlockInfoBase() { this->Clear(); } - - libCZI::CDimCoordinate coordinate; ///< The subblock's coordinate. - bool mIndexValid; ///< Whether the field 'mIndex' is valid; - int mIndex; ///< The M-index of the subblock. - int x; ///< The x-coordinate of the subblock. - int y; ///< The x-coordinate of the subblock. - int logicalWidth; ///< The logical with of the subblock (in pixels). - int logicalHeight; ///< The logical height of the subblock (in pixels). - int physicalWidth; ///< The physical with of the subblock (in pixels). - int physicalHeight; ///< The physical height of the subblock (in pixels). - libCZI::PixelType PixelType; ///< The pixel type of the subblock. + AddSubBlockInfoBase() { this->AddSubBlockInfoBase::Clear(); } + + virtual ~AddSubBlockInfoBase() = default; + + libCZI::CDimCoordinate coordinate; ///< The subblock's coordinate. + bool mIndexValid; ///< Whether the field 'mIndex' is valid; + int mIndex; ///< The M-index of the subblock. + int x; ///< The x-coordinate of the subblock. + int y; ///< The x-coordinate of the subblock. + int logicalWidth; ///< The logical with of the subblock (in pixels). + int logicalHeight; ///< The logical height of the subblock (in pixels). + int physicalWidth; ///< The physical with of the subblock (in pixels). + int physicalHeight; ///< The physical height of the subblock (in pixels). + libCZI::PixelType PixelType; ///< The pixel type of the subblock. + libCZI::SubBlockPyramidType pyramid_type; ///< The pyramid type of the subblock. The significance of this field + ///< is unclear (and considered legacy). It is recommended to have this + ///< field set to 'SubBlockPyramidType::None' (which is the value set as default). /// The compression-mode (applying to the subblock-data). If using a compressed format, the data /// passed in must be already compressed - the writer does _not_ perform the compression. @@ -474,6 +479,7 @@ namespace libCZI this->physicalWidth = 0; this->physicalHeight = 0; this->PixelType = libCZI::PixelType::Invalid; + this->pyramid_type = libCZI::SubBlockPyramidType::None; this->SetCompressionMode(CompressionMode::UnCompressed); } diff --git a/Src/libCZI_UnitTests/test_CziSubBlockDirectory.cpp b/Src/libCZI_UnitTests/test_CziSubBlockDirectory.cpp index 9e8f604f..c1be534c 100644 --- a/Src/libCZI_UnitTests/test_CziSubBlockDirectory.cpp +++ b/Src/libCZI_UnitTests/test_CziSubBlockDirectory.cpp @@ -23,6 +23,7 @@ struct SubBlockEntryData static CCziSubBlockDirectory::SubBlkEntry SubBlkEntryFromSubBlockEntryData(const SubBlockEntryData* ptrData) { CCziSubBlockDirectory::SubBlkEntry entry; + entry.Invalidate(); entry.coordinate = CDimCoordinate::Parse(ptrData->coordinate); entry.mIndex = ptrData->mIndex; entry.x = ptrData->x; @@ -37,7 +38,6 @@ static CCziSubBlockDirectory::SubBlkEntry SubBlkEntryFromSubBlockEntryData(const return entry; } - TEST(CziSubBlockDirectory, CziSubBlockDirectory1) { static const SubBlockEntryData data[] = diff --git a/Src/libCZI_UnitTests/test_readerwriter.cpp b/Src/libCZI_UnitTests/test_readerwriter.cpp index b5c3f0f7..6dfe0749 100644 --- a/Src/libCZI_UnitTests/test_readerwriter.cpp +++ b/Src/libCZI_UnitTests/test_readerwriter.cpp @@ -254,7 +254,7 @@ TEST(CziReaderWriter, ReaderWriter1) memcpy(pBuffer.get(), sblkdata.get(), sizeSbblkData); for (size_t i = 0; i < sizeSbblkData; ++i) { - ++* (static_cast(pBuffer.get()) + i); + ++*(static_cast(pBuffer.get()) + i); } addSbInfo.ptrData = pBuffer.get(); @@ -324,18 +324,18 @@ TEST(CziReaderWriter, ReaderWriter2) addSbInfo.sizeData = sizeSbblkData * 4; addSbInfo.getData = [&](int callCnt, size_t offset, const void*& ptr, size_t& size)->bool - { - // repeat every byte four times - auto byteNo = callCnt / 4; - if (size_t(byteNo) < sizeSbblkData) { - ptr = ((const uint8_t*)sblkdata.get()) + byteNo; - size = 1; - return true; - } + // repeat every byte four times + auto byteNo = callCnt / 4; + if (size_t(byteNo) < sizeSbblkData) + { + ptr = ((const uint8_t*)sblkdata.get()) + byteNo; + size = 1; + return true; + } - return false; - }; + return false; + }; rw->ReplaceSubBlock(0, addSbInfo); @@ -1263,3 +1263,92 @@ TEST(CziReaderWriter, ReaderWriterEmpty3) EXPECT_TRUE(strcmp(xmlRead.c_str(), expectedResult) == 0) << "Incorrect result"; } + + +struct SparePyramidTypeFixture : public testing::TestWithParam { }; + +TEST_P(SparePyramidTypeFixture, ReaderWriterCheckDeprecatedSparePyramidType) +{ + const auto pyramid_type_used_for_test = GetParam(); + + auto writer = CreateCZIWriter(); + auto outStream = make_shared(0); + + auto spWriterInfo = make_shared( + GUID{ 0x1234567,0x89ab,0xcdef,{ 1,2,3,4,5,6,7,8 } }, + CDimBounds{ { DimensionIndex::C, 0, 1 } }, // set a bounds for C + 0, 3); // set a bounds M : 0<=m<=0 + writer->Create(outStream, spWriterInfo); + + auto bitmap = CreateGray8BitmapAndFill(2, 2, 0x1); + AddSubBlockInfoStridedBitmap addSbBlkInfo; + addSbBlkInfo.Clear(); + addSbBlkInfo.coordinate.Set(DimensionIndex::C, 0); + addSbBlkInfo.mIndexValid = true; + addSbBlkInfo.mIndex = 0; + addSbBlkInfo.x = 0; + addSbBlkInfo.y = 0; + addSbBlkInfo.logicalWidth = bitmap->GetWidth(); + addSbBlkInfo.logicalHeight = bitmap->GetHeight(); + addSbBlkInfo.physicalWidth = bitmap->GetWidth(); + addSbBlkInfo.physicalHeight = bitmap->GetHeight(); + addSbBlkInfo.pyramid_type = pyramid_type_used_for_test; + addSbBlkInfo.PixelType = bitmap->GetPixelType(); + { + ScopedBitmapLockerSP lock_info_bitmap{ bitmap }; + addSbBlkInfo.ptrBitmap = lock_info_bitmap.ptrDataRoi; + addSbBlkInfo.strideBitmap = lock_info_bitmap.stride; + writer->SyncAddSubBlock(addSbBlkInfo); + } + + PrepareMetadataInfo prepare_metadata_info; + auto metaDataBuilder = writer->GetPreparedMetadata(prepare_metadata_info); + WriteMetadataInfo write_metadata_info; + write_metadata_info.Clear(); + const auto& strMetadata = metaDataBuilder->GetXml(); + write_metadata_info.szMetadata = strMetadata.c_str(); + write_metadata_info.szMetadataSize = strMetadata.size() + 1; + write_metadata_info.ptrAttachment = nullptr; + write_metadata_info.attachmentSize = 0; + writer->SyncWriteMetadata(write_metadata_info); + writer->Close(); + writer.reset(); + + size_t czi_document_size = 0; + shared_ptr czi_document_data = outStream->GetCopy(&czi_document_size); + + const auto memory_stream = make_shared(czi_document_data.get(), czi_document_size); + const auto reader = CreateCZIReader(); + reader->Open(memory_stream); + + uint32_t call_count = 0; + bool subblock_looks_as_expected = false; + reader->EnumerateSubBlocks( + [&](int index, const SubBlockInfo& info)->bool + { + // we expect to find only one subblock, and this has to have the expected + // "deprecated pyramid-type" + if (info.pyramidType == pyramid_type_used_for_test) + { + subblock_looks_as_expected = true; + } + + ++call_count; + return true; + }); + + EXPECT_TRUE(call_count == 1 && subblock_looks_as_expected) << "document does not contain the expected subblock"; + + auto sub_block = reader->ReadSubBlock(0); + auto info = sub_block->GetSubBlockInfo(); + + EXPECT_TRUE(info.pyramidType == pyramid_type_used_for_test); +} + +INSTANTIATE_TEST_SUITE_P( + CziReaderWriter, + SparePyramidTypeFixture, + testing::Values( + SubBlockPyramidType::None, + SubBlockPyramidType::SingleSubBlock, + SubBlockPyramidType::MultiSubBlock));