Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

non-x-y-subblocks #63

Merged
merged 10 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.50.1
VERSION 0.51.0
HOMEPAGE_URL "https://github.com/ZEISS/libczi"
DESCRIPTION "libCZI is an Open Source Cross-Platform C++ library to read and write CZI")

Expand Down
70 changes: 38 additions & 32 deletions Src/libCZI/CZIReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,21 @@
{
}

/*virtual */void CCZIReader::Open(std::shared_ptr<libCZI::IStream> stream)
/*virtual */void CCZIReader::Open(const std::shared_ptr<libCZI::IStream>& stream, const ICZIReader::OpenOptions* options)
ptahmose marked this conversation as resolved.
Show resolved Hide resolved
{
if (this->isOperational == true)
{
throw logic_error("CZIReader is already operational.");
}

if (options == nullptr)
{
auto default_options = OpenOptions{};
return CCZIReader::Open(stream, &default_options);
}

this->hdrSegmentData = CCZIParse::ReadFileHeaderSegmentData(stream.get());
this->subBlkDir = CCZIParse::ReadSubBlockDirectory(stream.get(), this->hdrSegmentData.GetSubBlockDirectoryPosition());
this->subBlkDir = CCZIParse::ReadSubBlockDirectory(stream.get(), this->hdrSegmentData.GetSubBlockDirectoryPosition(), options->lax_subblock_coordinate_checks);
auto attachmentPos = this->hdrSegmentData.GetAttachmentDirectoryPosition();
if (attachmentPos != 0)
{
Expand Down Expand Up @@ -84,13 +90,13 @@
[&](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;
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;
return funcEnum(index, info);
});
}

Expand All @@ -101,14 +107,14 @@
[&](int index, const CCziSubBlockDirectory::SubBlkEntry& entry)->bool
{
DirectorySubBlockInfo 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.filePosition = entry.FilePosition;
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.filePosition = entry.FilePosition;
return funcEnum(index, info);

Check warning on line 117 in Src/libCZI/CZIReader.cpp

View check run for this annotation

Codecov / codecov/patch

Src/libCZI/CZIReader.cpp#L110-L117

Added lines #L110 - L117 were not covered by tests
});
}

Expand Down Expand Up @@ -137,7 +143,7 @@
}
}

return true;
return true;
});
}

Expand Down Expand Up @@ -169,8 +175,8 @@
[&](int index, const SubBlockInfo& sbinfo)->bool
{
info = sbinfo;
foundASubBlock = true;
return false;
foundASubBlock = true;
return false;

Check warning on line 179 in Src/libCZI/CZIReader.cpp

View check run for this annotation

Codecov / codecov/patch

Src/libCZI/CZIReader.cpp#L178-L179

Added lines #L178 - L179 were not covered by tests
});
}
else
Expand All @@ -179,14 +185,14 @@
[&](int index, const SubBlockInfo& sbinfo)->bool
{
int c;
if (sbinfo.coordinate.TryGetPosition(DimensionIndex::C, &c) == true && c == channelIndex)
{
info = sbinfo;
foundASubBlock = true;
return false;
}
if (sbinfo.coordinate.TryGetPosition(DimensionIndex::C, &c) == true && c == channelIndex)

Check warning on line 188 in Src/libCZI/CZIReader.cpp

View check run for this annotation

Codecov / codecov/patch

Src/libCZI/CZIReader.cpp#L188

Added line #L188 was not covered by tests
{
info = sbinfo;
foundASubBlock = true;
return false;

Check warning on line 192 in Src/libCZI/CZIReader.cpp

View check run for this annotation

Codecov / codecov/patch

Src/libCZI/CZIReader.cpp#L190-L192

Added lines #L190 - L192 were not covered by tests
}

return true;
return true;

Check warning on line 195 in Src/libCZI/CZIReader.cpp

View check run for this annotation

Codecov / codecov/patch

Src/libCZI/CZIReader.cpp#L195

Added line #L195 was not covered by tests
});
}

Expand Down Expand Up @@ -236,10 +242,10 @@
[&](int index, const CCziAttachmentsDirectory::AttachmentEntry& ae)
{
ai.contentGuid = ae.ContentGuid;
memcpy(ai.contentFileType, ae.ContentFileType, sizeof(ae.ContentFileType));
ai.name = ae.Name;
bool b = funcEnum(index, ai);
return b;
memcpy(ai.contentFileType, ae.ContentFileType, sizeof(ae.ContentFileType));
ai.name = ae.Name;
bool b = funcEnum(index, ai);
return b;
});
}

Expand All @@ -263,7 +269,7 @@
}
}

return true;
return true;

Check warning on line 272 in Src/libCZI/CZIReader.cpp

View check run for this annotation

Codecov / codecov/patch

Src/libCZI/CZIReader.cpp#L272

Added line #L272 was not covered by tests
});
}

Expand Down
2 changes: 1 addition & 1 deletion Src/libCZI/CZIReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class CCZIReader : public libCZI::ICZIReader, public std::enable_shared_from_thi
void EnumerateSubBlocksEx(const std::function<bool(int index, const libCZI::DirectorySubBlockInfo& info)>& funcEnum) override;

// interface ICZIReader
void Open(std::shared_ptr<libCZI::IStream> stream) override;
void Open(const std::shared_ptr<libCZI::IStream>& stream, const ICZIReader::OpenOptions* options) override;
libCZI::FileHeaderInfo GetFileHeaderInfo() override;
std::shared_ptr<libCZI::IMetadataSegment> ReadMetadataSegment() override;
std::shared_ptr<libCZI::IAccessor> CreateAccessor(libCZI::AccessorType accessorType) override;
Expand Down
86 changes: 61 additions & 25 deletions Src/libCZI/CziParse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,15 @@
return fileHdr;
}

/*static*/CCziSubBlockDirectory CCZIParse::ReadSubBlockDirectory(libCZI::IStream* str, std::uint64_t offset)
/*static*/CCziSubBlockDirectory CCZIParse::ReadSubBlockDirectory(libCZI::IStream* str, std::uint64_t offset, bool lax_subblock_coordinate_checks)
{
CCziSubBlockDirectory subBlkDir;
CCZIParse::ReadSubBlockDirectory(str, offset, subBlkDir);
CCZIParse::ReadSubBlockDirectory(str, offset, subBlkDir, lax_subblock_coordinate_checks);
subBlkDir.AddingFinished();
return subBlkDir;
}

/*static*/void CCZIParse::ReadSubBlockDirectory(libCZI::IStream* str, std::uint64_t offset, const std::function<void(const CCziSubBlockDirectoryBase::SubBlkEntry&)>& addFunc, SegmentSizes* segmentSizes /*= nullptr*/)
/*static*/void CCZIParse::ReadSubBlockDirectory(libCZI::IStream* str, std::uint64_t offset, const std::function<void(const CCziSubBlockDirectoryBase::SubBlkEntry&)>& addFunc, bool lax_subblock_coordinate_checks, SegmentSizes* segmentSizes /*= nullptr*/)
{
SubBlockDirectorySegment subBlckDirSegment;
std::uint64_t bytesRead;
Expand Down Expand Up @@ -152,20 +152,20 @@
}
else if (subBlkDirDV != nullptr)
{
CCZIParse::AddEntryToSubBlockDirectory(subBlkDirDV, addFunc);
CCZIParse::AddEntryToSubBlockDirectory(subBlkDirDV, addFunc, lax_subblock_coordinate_checks);
}
});
}

/*static*/void CCZIParse::ReadSubBlockDirectory(libCZI::IStream* str, std::uint64_t offset, CCziSubBlockDirectory& subBlkDir)
/*static*/void CCZIParse::ReadSubBlockDirectory(libCZI::IStream* str, std::uint64_t offset, CCziSubBlockDirectory& subBlkDir, bool lax_subblock_coordinate_checks)
{
CCZIParse::ReadSubBlockDirectory(str, offset, [&](const CCziSubBlockDirectoryBase::SubBlkEntry& e)->void {subBlkDir.AddSubBlock(e); });
CCZIParse::ReadSubBlockDirectory(str, offset, [&](const CCziSubBlockDirectoryBase::SubBlkEntry& e)->void {subBlkDir.AddSubBlock(e); }, lax_subblock_coordinate_checks, nullptr);
}

/*static*/CCziAttachmentsDirectory CCZIParse::ReadAttachmentsDirectory(libCZI::IStream* str, std::uint64_t offset)
{
CCziAttachmentsDirectory attDir;
CCZIParse::ReadAttachmentsDirectory(str, offset, [&](const CCziAttachmentsDirectoryBase::AttachmentEntry& ae)->void {attDir.AddAttachmentEntry(ae); });
CCZIParse::ReadAttachmentsDirectory(str, offset, [&](const CCziAttachmentsDirectoryBase::AttachmentEntry& ae)->void {attDir.AddAttachmentEntry(ae); }, nullptr);
return attDir;
}

Expand Down Expand Up @@ -518,40 +518,76 @@
throw std::logic_error("not (yet) implemented");
}

/*static*/void CCZIParse::AddEntryToSubBlockDirectory(const SubBlockDirectoryEntryDV* subBlkDirDE, const std::function<void(const CCziSubBlockDirectoryBase::SubBlkEntry&)>& addFunc)
/*static*/void CCZIParse::AddEntryToSubBlockDirectory(const SubBlockDirectoryEntryDV* subBlkDirDV, const std::function<void(const CCziSubBlockDirectoryBase::SubBlkEntry&)>& addFunc, bool lax_subblock_coordinate_checks)
ptahmose marked this conversation as resolved.
Show resolved Hide resolved
{
CCziSubBlockDirectory::SubBlkEntry entry;
entry.Invalidate();

// TODO: - add consistency checks like dimension appears twice, X and Y are not present ...
for (int i = 0; i < subBlkDirDE->DimensionCount; ++i)
bool x_was_given = false;
bool y_was_given = false;
for (int i = 0; i < subBlkDirDV->DimensionCount; ++i)
{
if (CCZIParse::IsXDimension(subBlkDirDE->DimensionEntries[i].Dimension, 4))
if (CCZIParse::IsXDimension(subBlkDirDV->DimensionEntries[i].Dimension, 4))
{
entry.x = subBlkDirDE->DimensionEntries[i].Start;
entry.width = subBlkDirDE->DimensionEntries[i].Size;
entry.storedWidth = subBlkDirDE->DimensionEntries[i].StoredSize;
entry.x = subBlkDirDV->DimensionEntries[i].Start;
entry.width = subBlkDirDV->DimensionEntries[i].Size;
entry.storedWidth = subBlkDirDV->DimensionEntries[i].StoredSize;
x_was_given = true;
}
else if (CCZIParse::IsYDimension(subBlkDirDE->DimensionEntries[i].Dimension, 4))
else if (CCZIParse::IsYDimension(subBlkDirDV->DimensionEntries[i].Dimension, 4))
{
entry.y = subBlkDirDE->DimensionEntries[i].Start;
entry.height = subBlkDirDE->DimensionEntries[i].Size;
entry.storedHeight = subBlkDirDE->DimensionEntries[i].StoredSize;
entry.y = subBlkDirDV->DimensionEntries[i].Start;
entry.height = subBlkDirDV->DimensionEntries[i].Size;
entry.storedHeight = subBlkDirDV->DimensionEntries[i].StoredSize;
y_was_given = true;
}
else if (CCZIParse::IsMDimension(subBlkDirDE->DimensionEntries[i].Dimension, 4))
else if (CCZIParse::IsMDimension(subBlkDirDV->DimensionEntries[i].Dimension, 4))
{
entry.mIndex = subBlkDirDE->DimensionEntries[i].Start;
entry.mIndex = subBlkDirDV->DimensionEntries[i].Start;
if (!lax_subblock_coordinate_checks && subBlkDirDV->DimensionEntries[i].Size != 1)
{
stringstream string_stream;
string_stream << "Size for dimension 'M' is expected to be 1, but found " << subBlkDirDV->DimensionEntries[i].Size << " (file-offset:" << subBlkDirDV->FilePosition << ").";
throw LibCZICZIParseException(string_stream.str().c_str(), LibCZICZIParseException::ErrorCode::NonConformingSubBlockDimensionEntry);
}

Check warning on line 552 in Src/libCZI/CziParse.cpp

View check run for this annotation

Codecov / codecov/patch

Src/libCZI/CziParse.cpp#L552

Added line #L552 was not covered by tests
}
else
{
libCZI::DimensionIndex dim = CCZIParse::DimensionCharToDimensionIndex(subBlkDirDE->DimensionEntries[i].Dimension, 4);
entry.coordinate.Set(dim, subBlkDirDE->DimensionEntries[i].Start);
libCZI::DimensionIndex dim = CCZIParse::DimensionCharToDimensionIndex(subBlkDirDV->DimensionEntries[i].Dimension, 4);
entry.coordinate.Set(dim, subBlkDirDV->DimensionEntries[i].Start);
if (!lax_subblock_coordinate_checks && subBlkDirDV->DimensionEntries[i].Size != 1)
{
stringstream string_stream;
string_stream << "Size for dimension '" << Utils::DimensionToChar(dim) << "' is expected to be 1, but found " << subBlkDirDV->DimensionEntries[i].Size << " (file-offset:" << subBlkDirDV->FilePosition << ").";
throw LibCZICZIParseException(string_stream.str().c_str(), LibCZICZIParseException::ErrorCode::NonConformingSubBlockDimensionEntry);
}

Check warning on line 563 in Src/libCZI/CziParse.cpp

View check run for this annotation

Codecov / codecov/patch

Src/libCZI/CziParse.cpp#L563

Added line #L563 was not covered by tests
}
}

entry.FilePosition = subBlkDirDE->FilePosition;
entry.PixelType = subBlkDirDE->PixelType;
entry.Compression = subBlkDirDE->Compression;
if (!lax_subblock_coordinate_checks && (!x_was_given || !y_was_given))
{
stringstream string_stream;
string_stream << "No coordinate/size given for ";
if (!x_was_given && y_was_given)
{
string_stream << "'X'";
}
else if (!y_was_given && x_was_given)

Check warning on line 575 in Src/libCZI/CziParse.cpp

View check run for this annotation

Codecov / codecov/patch

Src/libCZI/CziParse.cpp#L575

Added line #L575 was not covered by tests
{
string_stream << "'Y'";
}

Check warning on line 578 in Src/libCZI/CziParse.cpp

View check run for this annotation

Codecov / codecov/patch

Src/libCZI/CziParse.cpp#L577-L578

Added lines #L577 - L578 were not covered by tests
else
{
string_stream << "'X' and 'Y'";

Check warning on line 581 in Src/libCZI/CziParse.cpp

View check run for this annotation

Codecov / codecov/patch

Src/libCZI/CziParse.cpp#L581

Added line #L581 was not covered by tests
}

string_stream << " (file-offset:" << subBlkDirDV->FilePosition << ").";
throw LibCZICZIParseException(string_stream.str().c_str(), LibCZICZIParseException::ErrorCode::NonConformingSubBlockDimensionEntry);
}

Check warning on line 586 in Src/libCZI/CziParse.cpp

View check run for this annotation

Codecov / codecov/patch

Src/libCZI/CziParse.cpp#L586

Added line #L586 was not covered by tests

entry.FilePosition = subBlkDirDV->FilePosition;
entry.PixelType = subBlkDirDV->PixelType;
entry.Compression = subBlkDirDV->Compression;

addFunc(entry);
}
Expand Down
22 changes: 17 additions & 5 deletions Src/libCZI/CziParse.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,25 @@ class CCZIParse
static FileHeaderSegmentData ReadFileHeaderSegment(libCZI::IStream* str);
static CFileHeaderSegmentData ReadFileHeaderSegmentData(libCZI::IStream* str);

static CCziSubBlockDirectory ReadSubBlockDirectory(libCZI::IStream* str, std::uint64_t offset);
/// Parse the subblock-directory from the specified stream at the specified offset.
/// Historically, libCZI did not check whether the elements in the dimensions-entry-list had a size other than
/// "1" given (for all dimensions other than X and Y). We refer to this as "lax parsing". If the argument
/// lax_subblock_coordinate_checks is true, then we check for those sizes to be as expected and otherwise
/// throw an exception.
///
/// \param [in,out] str The stream to read from.
/// \param offset The offset in the stream.
/// \param lax_subblock_coordinate_checks True to do "lax subblock coordinate checking".
///
/// \returns An in-memory representation of the subblock-directory.
static CCziSubBlockDirectory ReadSubBlockDirectory(libCZI::IStream* str, std::uint64_t offset, bool lax_subblock_coordinate_checks);

static CCziAttachmentsDirectory ReadAttachmentsDirectory(libCZI::IStream* str, std::uint64_t offset);
static void ReadAttachmentsDirectory(libCZI::IStream* str, std::uint64_t offset, const std::function<void(const CCziAttachmentsDirectoryBase::AttachmentEntry&)>& addFunc, SegmentSizes* segmentSizes = nullptr);
static void ReadAttachmentsDirectory(libCZI::IStream* str, std::uint64_t offset, const std::function<void(const CCziAttachmentsDirectoryBase::AttachmentEntry&)>& addFunc, SegmentSizes* segmentSizes);

static void ReadSubBlockDirectory(libCZI::IStream* str, std::uint64_t offset, CCziSubBlockDirectory& subBlkDir);
static void ReadSubBlockDirectory(libCZI::IStream* str, std::uint64_t offset, CCziSubBlockDirectory& subBlkDir, bool lax_subblock_coordinate_checks);

static void ReadSubBlockDirectory(libCZI::IStream* str, std::uint64_t offset, const std::function<void(const CCziSubBlockDirectoryBase::SubBlkEntry&)>& addFunc, SegmentSizes* segmentSizes = nullptr);
static void ReadSubBlockDirectory(libCZI::IStream* str, std::uint64_t offset, const std::function<void(const CCziSubBlockDirectoryBase::SubBlkEntry&)>& addFunc, bool lax_subblock_coordinate_checks, SegmentSizes* segmentSizes);

struct SubBlockStorageAllocate
{
Expand Down Expand Up @@ -98,7 +110,7 @@ class CCZIParse
static void ParseThroughDirectoryEntries(int count, const std::function<void(int, void*)>& funcRead, const std::function<void(const SubBlockDirectoryEntryDE*, const SubBlockDirectoryEntryDV*)>& funcAddEntry);

static void AddEntryToSubBlockDirectory(const SubBlockDirectoryEntryDE* subBlkDirDE, const std::function<void(const CCziSubBlockDirectoryBase::SubBlkEntry&)>& addFunc);
static void AddEntryToSubBlockDirectory(const SubBlockDirectoryEntryDV* subBlkDirDE, const std::function<void(const CCziSubBlockDirectoryBase::SubBlkEntry&)>& addFunc);
static void AddEntryToSubBlockDirectory(const SubBlockDirectoryEntryDV* subBlkDirDV, const std::function<void(const CCziSubBlockDirectoryBase::SubBlkEntry&)>& addFunc, bool lax_subblock_coordinate_checks);

static libCZI::DimensionIndex DimensionCharToDimensionIndex(const char* ptr, size_t size);
static bool IsMDimension(const char* ptr, size_t size);
Expand Down
1 change: 1 addition & 0 deletions Src/libCZI/CziReaderWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ void CCziReaderWriter::ReadCziStructure()
{
this->sbBlkDirectory.AddSubBlock(e);
},
true,
&sbBlkDirSegmentSize);

this->sbBlkDirectory.SetModified(false);
Expand Down
35 changes: 28 additions & 7 deletions Src/libCZI/libCZI.h
Original file line number Diff line number Diff line change
Expand Up @@ -618,17 +618,38 @@ namespace libCZI
};

/// This interface is used to represent the CZI-file.
class ICZIReader : public ISubBlockRepository, public ISubBlockRepositoryEx, public IAttachmentRepository
class LIBCZI_API ICZIReader : public ISubBlockRepository, public ISubBlockRepositoryEx, public IAttachmentRepository
{
public:
/// Opens the specified stream and reads the global information from the CZI-document.
/// The stream passed in will have its refcount incremented, a reference is held until Close
/// is called (or the instance is destroyed).
/// This structure gathers the settings for controlling the 'Open' operation of the CZIReader-class.
struct LIBCZI_API OpenOptions
{
/// This option controls whether the lax parameter validation when parsing the dimension-entry of a subblock is to be used.
/// Previous versions of libCZI did not check whether certain values in the file have the expected value. If those values
/// are different than expected, this meant that libCZI would not be able to deal with the document properly.
/// If lax checking of this is disabled, then Open will fail with a corresponding exception.
/// The default is to enable lax checking (for compatibility with previous libCZI-versions), but users are encouraged to
/// disable this for new code.
bool lax_subblock_coordinate_checks{ true };

/// Sets the the default.
void SetDefault()
{
this->lax_subblock_coordinate_checks = true;
}
};

/// Opens the specified stream and reads the global information from the CZI-document. The stream
/// passed in will have its refcount incremented, a reference is held until Close is called (or
/// the instance is destroyed).
///
/// \remark
/// If this method is called twice, then an exception of type std::logic_error is thrown.
/// If this method is called twice (assuming successful return), then an exception of type std::logic_error is
/// thrown.
///
/// \param stream The stream object.
virtual void Open(std::shared_ptr<IStream> stream) = 0;
/// \param stream The stream object.
/// \param options (Optional) Options for controlling the operation. If nullptr is given here, then the default settings are used.
virtual void Open(const std::shared_ptr<IStream>& stream, const OpenOptions* options = nullptr) = 0;

/// Gets the file header information.
/// \return The file header information.
Expand Down
Loading