From 429a54d4c34ba9310286de7515ab63f398df8afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Poizl?= Date: Thu, 26 Jan 2023 17:52:32 +0100 Subject: [PATCH] Add testing --- CMakeLists.txt | 20 +++- libxisf.cpp | 285 +++++++++++++++++++++++++-------------------- libxisf.h | 28 ++++- test/main.cpp | 91 +++++++++++++++ test/test.xisf | Bin 0 -> 5466 bytes test/test_lz4.xisf | Bin 0 -> 6134 bytes 6 files changed, 295 insertions(+), 129 deletions(-) create mode 100644 test/main.cpp create mode 100644 test/test.xisf create mode 100644 test/test_lz4.xisf diff --git a/CMakeLists.txt b/CMakeLists.txt index 04e2bb1..7d933a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.14) -project(libXISF LANGUAGES CXX) +project(libXISF VERSION 0.1.0 LANGUAGES CXX C) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) @@ -22,8 +22,24 @@ add_library(XISF SHARED lz4/lz4hc.h ) -target_link_libraries(XISF PRIVATE Qt${QT_VERSION_MAJOR}::Core) +target_link_libraries(XISF PUBLIC Qt${QT_VERSION_MAJOR}::Core) target_compile_definitions(XISF PRIVATE LIBXISF_LIBRARY) set_target_properties(XISF PROPERTIES PUBLIC_HEADER libxisf.h) + +include(GNUInstallDirs) + +install(TARGETS XISF PUBLIC_HEADER) + +#testing + +enable_testing() + +add_executable(LibXISFTest test/main.cpp) + +target_link_libraries(LibXISFTest XISF) + +add_test(NAME LibXISFTest COMMAND LibXISFTest) +add_test(NAME LibXISFTestRead COMMAND LibXISFTest "${CMAKE_CURRENT_LIST_DIR}/test/test.xisf") +add_test(NAME LibXISFTestReadLZ4 COMMAND LibXISFTest "${CMAKE_CURRENT_LIST_DIR}/test/test_lz4.xisf") diff --git a/libxisf.cpp b/libxisf.cpp index 08c8a28..5de1095 100644 --- a/libxisf.cpp +++ b/libxisf.cpp @@ -18,18 +18,16 @@ #include "libxisf.h" #include +#include #include #include #include #include #include #include -#include #include "lz4/lz4.h" #include "lz4/lz4hc.h" -#define STRING_ENUM(e) {#e, e} - #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) template<> struct std::hash { @@ -43,8 +41,14 @@ template<> struct std::hash namespace LibXISF { -static std::unordered_map typeToId; -static std::unordered_map idToType; +static std::unordered_map typeToId; +static std::unordered_map idToType; +static std::unordered_map imageTypeToEnum; +static std::unordered_map imageTypeToString; +static std::unordered_map sampleFormatToEnum; +static std::unordered_map sampleFormatToString; +static std::unordered_map colorSpaceToEnum; +static std::unordered_map colorSpaceToString; static void byteShuffle(QByteArray &data, int itemSize) { @@ -84,16 +88,9 @@ static void byteUnshuffle(QByteArray &data, int itemSize) } } -QString sampleFormatToString(Image::SampleFormat format) +bool isString(QMetaType::Type type) { - static QStringList sampleFormats = {"UInt8", "UInt16", "UInt32", "UInt64", "Float32", "Float64", "Complex32", "Complex64"}; - return sampleFormats[format]; -} - -QString colorSpaceToString(Image::ColorSpace colorSpace) -{ - static QStringList colorSpaces = {"Gray", "RGB", "CIELab"}; - return colorSpaces[colorSpace]; + return type == QMetaType::QString; } void DataBlock::decompress(const QByteArray &input, const QString &encoding) @@ -122,7 +119,7 @@ void DataBlock::decompress(const QByteArray &input, const QString &encoding) case LZ4HC: data.resize(uncompressedSize); if(LZ4_decompress_safe(tmp.constData(), data.data(), tmp.size(), data.size()) < 0) - throw std::runtime_error("LZ4 decompression failed"); + throw Error("LZ4 decompression failed"); break; } @@ -157,7 +154,7 @@ void DataBlock::compress() compSize = LZ4_compress_HC(tmp.constData(), data.data(), tmp.size(), data.size(), LZ4HC_CLEVEL_DEFAULT); if(compSize <= 0) - throw std::runtime_error("LZ4 compression failed"); + throw Error("LZ4 compression failed"); data.resize(compSize); break; @@ -165,6 +162,12 @@ void DataBlock::compress() } } +Property::Property(const QString &_id, const char *_value) : + id(_id), + value(_value) +{ +} + template void planarToNormal(void *_in, void *_out, size_t channels, size_t size) { @@ -187,8 +190,11 @@ void normalToPlanar(void *_in, void *_out, size_t channels, size_t size) void Image::convertPixelStorageTo(PixelStorage storage) { - if(pixelStorage == storage) + if(pixelStorage == storage || channelCount <= 1) + { + pixelStorage = storage; return; + } QByteArray tmp; tmp.resize(dataBlock.data.size()); @@ -231,22 +237,14 @@ void Image::convertPixelStorageTo(PixelStorage storage) Image::Type Image::imageTypeEnum(const QString &type) { - static const std::unordered_map imageTypeMap = {STRING_ENUM(Bias), - STRING_ENUM(Dark), - STRING_ENUM(Flat), - STRING_ENUM(Light), - STRING_ENUM(MasterBias), - STRING_ENUM(MasterDark), - STRING_ENUM(MasterFlat), - STRING_ENUM(DefectMap), - STRING_ENUM(RejectionMapHigh), - STRING_ENUM(RejectionMapLow), - STRING_ENUM(BinaryRejectionMapHigh), - STRING_ENUM(BinaryRejectionMapLow), - STRING_ENUM(SlopeMap), - STRING_ENUM(WeightMap}); - auto t = imageTypeMap.find(type); - return t != imageTypeMap.end() ? t->second : Image::Light; + auto t = imageTypeToEnum.find(type); + return t != imageTypeToEnum.end() ? t->second : Image::Light; +} + +QString Image::imageTypeString(Type type) +{ + auto t = imageTypeToString.find(type); + return t != imageTypeToString.end() ? t->second : "Light"; } Image::PixelStorage Image::pixelStorageEnum(const QString &storage) @@ -255,25 +253,34 @@ Image::PixelStorage Image::pixelStorageEnum(const QString &storage) return Image::Planar; } +QString Image::pixelStorageString(PixelStorage storage) +{ + if(storage == Normal)return "Normal"; + return "Planar"; +} + Image::SampleFormat Image::sampleFormatEnum(const QString &format) { - static const std::unordered_map sampleFormatMap = {STRING_ENUM(UInt8), - STRING_ENUM(UInt16), - STRING_ENUM(UInt32), - STRING_ENUM(UInt64), - STRING_ENUM(Float32), - STRING_ENUM(Float64), - STRING_ENUM(Complex32), - STRING_ENUM(Complex64)}; - auto t = sampleFormatMap.find(format); - return t != sampleFormatMap.end() ? t->second : Image::UInt16; + auto t = sampleFormatToEnum.find(format); + return t != sampleFormatToEnum.end() ? t->second : Image::UInt16; +} + +QString Image::sampleFormatString(SampleFormat format) +{ + auto t = sampleFormatToString.find(format); + return t != sampleFormatToString.end() ? t->second : "UInt16"; } Image::ColorSpace Image::colorSpaceEnum(const QString &colorSpace) { - static const std::unordered_map colorSpaceMap = { STRING_ENUM(Gray), STRING_ENUM(RGB), STRING_ENUM(CIELab) }; - auto t = colorSpaceMap.find(colorSpace); - return t != colorSpaceMap.end() ? t->second : Image::Gray; + auto t = colorSpaceToEnum.find(colorSpace); + return t != colorSpaceToEnum.end() ? t->second : Image::Gray; +} + +QString Image::colorSpaceString(ColorSpace colorSpace) +{ + auto t = colorSpaceToString.find(colorSpace); + return t != colorSpaceToString.end() ? t->second : "Gray"; } XISFReader::XISFReader() @@ -299,7 +306,7 @@ void XISFReader::open(QIODevice *io) close(); _io.reset(io); if(!_io->open(QIODevice::ReadOnly)) - throw std::runtime_error("Failed to open file"); + throw Error("Failed to open file"); readSignature(); readXISFHeader(); @@ -321,7 +328,7 @@ int XISFReader::imagesCount() const const Image& XISFReader::getImage(uint32_t n) { if(n >= _images.size()) - throw std::runtime_error("Out of bounds"); + throw Error("Out of bounds"); Image &img = _images[n]; if(img.dataBlock.attachmentPos) @@ -355,20 +362,20 @@ void XISFReader::readXISFHeader() _properties.push_back(readPropertyElement()); } } - else throw std::runtime_error("Unknown root XML element"); + else throw Error("Unknown root XML element"); if(_xml->hasError()) - throw std::runtime_error(_xml->errorString().toStdString()); + throw Error(_xml->errorString().toStdString()); } void XISFReader::readSignature() { char signature[8]; if(_io->read(signature, sizeof(signature)) != sizeof(signature)) - throw std::runtime_error("Failed to read from file"); + throw Error("Failed to read from file"); if(memcmp(signature, "XISF0100", sizeof(signature)) != 0) - throw std::runtime_error("Not valid XISF 1.0 file"); + throw Error("Not valid XISF 1.0 file"); } void XISFReader::readImageElement() @@ -378,11 +385,11 @@ void XISFReader::readImageElement() Image image; QVector geometry = attributes.value("geometry").split(":"); - if(geometry.size() != 3)throw std::runtime_error("We support only 2D images"); + if(geometry.size() != 3)throw Error("We support only 2D images"); image.width = geometry[0].toULongLong(); image.height = geometry[1].toULongLong(); image.channelCount = geometry[2].toULongLong(); - if(!image.width || !image.height || !image.channelCount)throw std::runtime_error("Invalid image geometry"); + if(!image.width || !image.height || !image.channelCount)throw Error("Invalid image geometry"); QVector bounds = attributes.value("bounds").split(":"); if(bounds.size() == 2) @@ -403,8 +410,6 @@ void XISFReader::readImageElement() { if(_xml->name() == "Property") image.properties.push_back(readPropertyElement()); - else if(image.dataBlock.embedded && _xml->name() == "Data") - readDataElement(image.dataBlock); else if(_xml->name() == "ICCProfile") { DataBlock icc = readDataBlock(); @@ -426,53 +431,29 @@ Property XISFReader::readPropertyElement() property.format = attributes.value("format").toString(); property.comment = attributes.value("comment").toString(); - QStringRef type = attributes.value("type"); - QStringRef value = attributes.value("value"); - if(type == "Int8") - property.value.setValue((Int8)value.toInt()); - else if(type == "Int16") - property.value.setValue((Int16)value.toInt()); - else if(type == "Int32") - property.value.setValue((Int32)value.toInt()); - else if(type == "Int64") - property.value.setValue((Int64)value.toLongLong()); - else if(type == "UInt8") - property.value.setValue((Int8)value.toInt()); - else if(type == "UInt16") - property.value.setValue((Int16)value.toInt()); - else if(type == "UInt32") - property.value.setValue(value.toUInt()); - else if(type == "UInt64") - property.value.setValue(value.toULongLong()); - else if(type == "Float32") - property.value = value.toFloat(); - else if(type == "Float64") - property.value = value.toDouble(); - else if(type == "TimePoint") - property.value = QDateTime::fromString(value.toString(), Qt::ISODate); - else if(type == "String") - { - if(attributes.hasAttribute("location")) - { - DataBlock dataBlock = readDataBlock(); - if(dataBlock.embedded) - readDataElement(dataBlock); - property.value = QString::fromUtf8(dataBlock.data); - } - else - property.value = _xml->readElementText(); - } - else - property.value = value.toString(); + QString type = attributes.value("type").toString(); + if(typeToId.count(type) == 0) + throw Error("Invalid type in property"); - qDebug() << property.id << type << property.value.typeName() << property.value; + QVariant value = attributes.value("value").toString(); + value.convert(typeToId[type]); + property.value = value; return property; } void XISFReader::readDataElement(DataBlock &dataBlock) { - readCompression(dataBlock); + _xml->readNextStartElement(); + if(_xml->name() == "Data") + { + readCompression(dataBlock); + QString encoding = _xml->attributes().value("encoding").toString(); + QByteArray text = _xml->readElementText().toUtf8(); + dataBlock.decompress(text, encoding); + } + else + throw Error("Unexpected XML element"); } DataBlock XISFReader::readDataBlock() @@ -497,13 +478,16 @@ DataBlock XISFReader::readDataBlock() bool ok1, ok2; dataBlock.attachmentPos = location[1].toULongLong(&ok1); dataBlock.attachmentSize = location[2].toULongLong(&ok2); - if(!ok1 || !ok2)throw std::runtime_error("Invalid attachment"); + if(!ok1 || !ok2)throw Error("Invalid attachment"); } else { - throw std::runtime_error("Invalid data block"); + throw Error("Invalid data block"); } + if(dataBlock.embedded) + readDataElement(dataBlock); + return dataBlock; } @@ -519,7 +503,7 @@ void XISFReader::readCompression(DataBlock &dataBlock) else if(compression[0].startsWith("lz4")) dataBlock.codec = DataBlock::LZ4; else - throw std::runtime_error("Unknown compression codec"); + throw Error("Unknown compression codec"); dataBlock.uncompressedSize = compression[1].toULongLong(); @@ -528,7 +512,7 @@ void XISFReader::readCompression(DataBlock &dataBlock) if(compression.size() == 3) dataBlock.byteShuffling = compression[2].toInt(); else - throw std::runtime_error("Missing byte shuffling size"); + throw Error("Missing byte shuffling size"); } } } @@ -536,6 +520,7 @@ void XISFReader::readCompression(DataBlock &dataBlock) XISFWriter::XISFWriter() { _xml = std::make_unique(); + _xml->setAutoFormatting(true); } void XISFWriter::save(const QString &name) @@ -543,7 +528,7 @@ void XISFWriter::save(const QString &name) QFile fw(name); if(!fw.open(QIODevice::WriteOnly)) - throw std::runtime_error("Failed to open file"); + throw Error("Failed to open file"); save(fw); } @@ -567,6 +552,15 @@ void XISFWriter::save(QIODevice &io) } } +void XISFWriter::saveXML(const QString &name) +{ + QFile fw(name); + fw.open(QIODevice::WriteOnly); + QByteArray header = _xisfHeader.mid(16); + header.truncate(header.indexOf('\0')); + fw.write(header); +} + void XISFWriter::writeImage(const Image &image) { _images.push_back(image); @@ -599,29 +593,42 @@ void XISFWriter::writeHeader() writeMetadata(); _xml->writeEndElement(); + _xml->writeEndDocument(); uint32_t size = _xisfHeader.size(); - QByteArray blockPos = QByteArray::number(size); - _xisfHeader.replace("#########", blockPos); + + uint32_t offset = 0; + const char replace[] = "attachment:2147483648"; + for(auto &image : _images) + { + QByteArray blockPos = QByteArray("attachment:") + QByteArray::number(size + offset); + _xisfHeader.replace(_xisfHeader.indexOf(replace), sizeof(replace) - 1, blockPos); + offset += image.dataBlock.data.size(); + } + uint32_t headerSize = _xisfHeader.size() - sizeof(signature); _xisfHeader.append(size - _xisfHeader.size(), '\0'); - buffer.seek(8); buffer.write((char*)&headerSize, sizeof(size)); - _xml->writeEndDocument(); - if(_xml->hasError()) - throw std::runtime_error("Failed to write XML header"); + throw Error("Failed to write XML header"); } void XISFWriter::writeImageElement(const Image &image) { _xml->writeStartElement("Image"); _xml->writeAttribute("geometry", QString("%1:%2:%3").arg(image.width).arg(image.height).arg(image.channelCount)); - _xml->writeAttribute("sampleFormat", sampleFormatToString(image.sampleFormat)); - _xml->writeAttribute("colorSpace", colorSpaceToString(image.colorSpace)); + _xml->writeAttribute("sampleFormat", Image::sampleFormatString(image.sampleFormat)); + _xml->writeAttribute("colorSpace", Image::colorSpaceString(image.colorSpace)); + _xml->writeAttribute("imageType", Image::imageTypeString(image.imageType)); + if((image.sampleFormat == Image::Float32 || image.sampleFormat == Image::Float64) || + image.bounds[0] != 0.0 || image.bounds[1] != 1.0) + { + _xml->writeAttribute("bounds", QString("%1:%2").arg(image.bounds[0])); + } + writeDataBlockAttributes(image.dataBlock); for(auto &property : image.properties) writePropertyElement(property); @@ -643,7 +650,7 @@ void XISFWriter::writeDataBlockAttributes(const DataBlock &dataBlock) } else { - _xml->writeAttribute("location", QString("attachment:#########:%1").arg(dataBlock.data.size())); + _xml->writeAttribute("location", QString("attachment:2147483648:%1").arg(dataBlock.data.size())); } } @@ -673,9 +680,11 @@ void XISFWriter::writeCompressionAttributes(const DataBlock &dataBlock) void XISFWriter::writePropertyElement(const Property &property) { + int type = property.value.userType(); + _xml->writeStartElement("Property"); _xml->writeAttribute("id", property.id); - _xml->writeAttribute("type", idToType[property.value.type()]); + _xml->writeAttribute("type", idToType[type]); if(!property.format.isEmpty()) _xml->writeAttribute("format", property.format); @@ -683,34 +692,34 @@ void XISFWriter::writePropertyElement(const Property &property) if(!property.comment.isEmpty()) _xml->writeAttribute("comment", property.comment); - if((QMetaType::Type)property.value.type() == QMetaType::QString) + if(type == QMetaType::QString) _xml->writeCharacters(property.value.toString()); + else if(type == QMetaType::SChar || type == QMetaType::UChar) + _xml->writeAttribute("value", QString::number(property.value.toInt())); else _xml->writeAttribute("value", property.value.toString()); _xml->writeEndElement(); + if(_xml->hasError()) + throw Error("Failed to write property"); } void XISFWriter::writeMetadata() { _xml->writeStartElement("Metadata"); - - writePropertyElement({"XISF:CreationTime", QDateTime::currentDateTimeUtc().toString(Qt::ISODate), QString(), QString()}); - - _xml->writeStartElement("Property"); - _xml->writeAttribute("id", "XISF:CreatorApplication"); - _xml->writeAttribute("type", "String"); - _xml->writeCharacters("LibXISF"); - _xml->writeEndElement(); - + writePropertyElement(Property("XISF:CreationTime", QDateTime::currentDateTimeUtc().toString(Qt::ISODate))); + writePropertyElement(Property("XISF:CreatorApplication", "LibXISF")); _xml->writeEndElement(); } #define REGISTER_METATYPE(type) { int id = qRegisterMetaType("LibXISF::"#type); \ typeToId.insert({#type, id}); idToType.insert({id, #type}); } -struct TypesInit +#define STRING_ENUM(map, map2, c, e) { map.insert({#e, c::e}); map2.insert({c::e, #e}); } +//#define ENUM_STRING(e) {#e, e} + +struct Init { - TypesInit() + Init() { REGISTER_METATYPE(Boolean); REGISTER_METATYPE(Int8); @@ -751,6 +760,34 @@ struct TypesInit REGISTER_METATYPE(C64Matrix); REGISTER_METATYPE(String); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, Bias); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, Dark); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, Flat); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, Light); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, MasterBias); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, MasterDark); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, MasterFlat); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, DefectMap); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, RejectionMapHigh); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, RejectionMapLow); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, BinaryRejectionMapHigh); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, BinaryRejectionMapLow); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, SlopeMap); + STRING_ENUM(imageTypeToEnum, imageTypeToString, Image, WeightMap); + + STRING_ENUM(sampleFormatToEnum, sampleFormatToString, Image, UInt8); + STRING_ENUM(sampleFormatToEnum, sampleFormatToString, Image, UInt16); + STRING_ENUM(sampleFormatToEnum, sampleFormatToString, Image, UInt32); + STRING_ENUM(sampleFormatToEnum, sampleFormatToString, Image, UInt64); + STRING_ENUM(sampleFormatToEnum, sampleFormatToString, Image, Float32); + STRING_ENUM(sampleFormatToEnum, sampleFormatToString, Image, Float64); + STRING_ENUM(sampleFormatToEnum, sampleFormatToString, Image, Complex32); + STRING_ENUM(sampleFormatToEnum, sampleFormatToString, Image, Complex64); + + STRING_ENUM(colorSpaceToEnum, colorSpaceToString, Image, Gray); + STRING_ENUM(colorSpaceToEnum, colorSpaceToString, Image, RGB); + STRING_ENUM(colorSpaceToEnum, colorSpaceToString, Image, CIELab); + QMetaType::registerConverter([](const Complex32 &c){ return QString("(%1,%2)").arg(c.real).arg(c.imag); }); QMetaType::registerConverter([](const Complex64 &c){ return QString("(%1,%2)").arg(c.real).arg(c.imag); }); QMetaType::registerConverter([](QString s) @@ -776,6 +813,6 @@ struct TypesInit } }; -static TypesInit typesInit; +static Init init; } diff --git a/libxisf.h b/libxisf.h index 1d83d67..1979c86 100644 --- a/libxisf.h +++ b/libxisf.h @@ -56,6 +56,14 @@ struct Property QVariant value; QString comment; QString format; + + Property() = default; + Property(const Property &) = default; + Property(const QString &_id, const char *_value); + template + Property(const QString &_id, const T& _value) : + id(_id), + value(QVariant::fromValue(_value)){} }; struct Image @@ -102,7 +110,7 @@ struct Image uint64_t width = 0; uint64_t height = 0; - uint64_t channelCount = 0; + uint64_t channelCount = 1; double bounds[2] = {0.0, 1.0}; Type imageType = Light; PixelStorage pixelStorage = Planar; @@ -115,9 +123,13 @@ struct Image void convertPixelStorageTo(PixelStorage storage); static Type imageTypeEnum(const QString &type); + static QString imageTypeString(Type type); static PixelStorage pixelStorageEnum(const QString &storage); + static QString pixelStorageString(PixelStorage storage); static SampleFormat sampleFormatEnum(const QString &format); + static QString sampleFormatString(SampleFormat format); static ColorSpace colorSpaceEnum(const QString &colorSpace); + static QString colorSpaceString(ColorSpace colorSpace); }; class LIBXISF_EXPORT XISFReader @@ -126,6 +138,7 @@ public: XISFReader(); void open(const QString &name); void open(const QByteArray &data); + /** Open image from */ void open(QIODevice *io); void close(); int imagesCount() const; @@ -152,6 +165,7 @@ public: void save(const QString &name); void save(QByteArray &data); void save(QIODevice &io); + void saveXML(const QString &name); void writeImage(const Image &image); private: void writeHeader(); @@ -224,9 +238,17 @@ typedef Matrix C32Matrix; typedef Matrix C64Matrix; typedef QString String; -} +class LIBXISF_EXPORT Error : public std::exception +{ + std::string _msg; +public: + Error() = default; + explicit Error(const char *msg) : Error(std::string(msg)) {} + explicit Error(const std::string &msg) : std::exception(), _msg(msg) {} + const char* what() const noexcept { return _msg.c_str(); } +}; -QDebug operator<<(QDebug dbg, const LibXISF::Complex32 &c); +} Q_DECLARE_METATYPE(LibXISF::Boolean); Q_DECLARE_METATYPE(LibXISF::Int8); diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..95ff4e1 --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,91 @@ +/************************************************************************ + * LibXISF - library to load and save XISF files * + * Copyright (C) 2023 DuĊĦan Poizl * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see .* + ************************************************************************/ + +#include +#include "libxisf.h" + +using namespace LibXISF; + +#define TEST(cond, msg) if(cond){ std::cerr << msg << std::endl; return 1; } + +int main(int argc, char **argv) +{ + try + { + if (argc < 2) + { + XISFWriter writer; + Image image; + image.width = 5; + image.height = 7; + image.imageType = Image::Light; + image.dataBlock.data.resize(image.width*image.height*2); + image.properties.push_back(Property("PropertyString", "Hello XISF")); + image.properties.push_back(Property("PropertyBoolean", (Boolean)true)); + image.properties.push_back(Property("PropertyInt8", (Int8)(8))); + image.properties.push_back(Property("PropertyInt16", (Int16)16)); + image.properties.push_back(Property("PropertyInt32", 32)); + image.properties.push_back(Property("PropertyUInt8", (UInt8)8)); + image.properties.push_back(Property("PropertyUInt16", (UInt16)(16))); + image.properties.push_back(Property("PropertyUInt32", (uint32_t)32)); + image.properties.push_back(Property("PropertyFloat32", (Float32) 0.32)); + image.properties.push_back(Property("PropertyFloat64", (Float64) 0.64)); + image.properties.push_back(Property("PropertyComplex32", Complex32{3.0, -2.0})); + image.properties.push_back(Property("PropertyComplex64", Complex64{-3.0, 2.0})); + writer.writeImage(image); + + image.imageType = Image::Flat; + image.dataBlock.codec = DataBlock::LZ4; + image.dataBlock.byteShuffling = 2; + writer.writeImage(image); + QByteArray data; + std::cout << "Saving image" << std::endl; + writer.save(data); + + XISFReader reader; + std::cout << "Loading image" << std::endl; + reader.open(data); + const Image &img0 = reader.getImage(0); + const Image &img1 = reader.getImage(1); + TEST(image.properties.size() != img0.properties.size(), "Property count doesn't match"); + TEST(image.dataBlock.data != img0.dataBlock.data, "Images doesn't match"); + TEST(img0.dataBlock.data != img1.dataBlock.data, "Images doesn't match"); + } + else + { + LibXISF::XISFReader reader; + reader.open(QString(argv[1])); + TEST(reader.imagesCount() != 1, "No image"); + + const LibXISF::Image &image = reader.getImage(0); + TEST(image.width != 8, "Invalid width") + TEST(image.height != 10, "Invalid height"); + TEST(image.colorSpace != LibXISF::Image::Gray, "Invalid color space"); + TEST(image.pixelStorage != LibXISF::Image::Planar, "Invalid pixel storage"); + //TEST(image.dataBlock.codec != LibXISF::DataBlock::None, "Invalid compression codec"); + TEST(!image.dataBlock.embedded, "Not embedded"); + TEST(image.dataBlock.data.size() != 80*2, "Invalid data size"); + } + } + catch (const LibXISF::Error &e) + { + std::cout << e.what() << std::endl; + return 2; + } + return 0; +} diff --git a/test/test.xisf b/test/test.xisf new file mode 100644 index 0000000000000000000000000000000000000000..c93810338165dc2a3994b55db692361d1eb579c9 GIT binary patch literal 5466 zcmbVQTX*6(65i*2-*>GahRivQ@g)#ofH?w8+F5SF&NS&a$5y}-UpTgCpnv_A1lS^L zlb+@QFjRf2`l?h_($cg>imE6N|Na+$b^hFk0s2mo)Q_XCq{@nfNaV(zAFaC5lRc_7 zr9XR}|5U4=j6Ee$>c0d8nW3{H$RdgF1pW_)f-V}xN$5~iMN3%XjEDg6(;y)ZB_7)O zbdBcz-UO7@nxZs*r8_4fptGiQ)2`Jv{@y?SDZ6o4`&6y=I(t8TJ;R+01e2q*E1i^f zyPf<_k?BZ?i?#Que<8Zlq!4H~BdRte|w}1ckJ1~ z{S|)Ta*y~PV-pX!+yvttlY3481nZl}f^?sZ0{ZT*)Vc5&jN$O(ZX z%bd)|hJ?O5!4@8E#$Xtdh<2q{AJW!~NeVwjuaK&U#)u&x7I)7{JgHU$**ERExy&Gs zQ8{5oTZln|;#c(I(-dvsyT?KSDMEr`xG9F+k)dgVfjJn*aP}m<0s$#=aD=4u32}Ol%pK(L^PA;<}B@~6ufe}v~KSJbKN8+f& z!NF`YF{S`@vQ4mzW8RDcvKPvboj6gti4%&fLrO{bJ7V#LQUu<6APSH>WZD0m7>LFUpm_y=*`vTDg3OxHj_3A{DH z!mfbS$s!H(INJa!u?uxPTA^3RrEyXWGsHHYnA6NoMHYZ2e&mPS@EEcy`Z(W8tqXl; z|B*hdooD^~#nE8xL=g$f(tU!Lx$^s!KU6lbu=K60G_<<1QonGXc>c!_ zmOUIAf8}22vR2nMMX%qsbWLyac5y09)~TeSA^GkfyYmP9EM{M4i=nZ|qFb?N^XhQ+ zby^WT&7=xTr8%|i#nU9Ozm>sYh%AU};6PT1K$&PEkZp_&YcQJ&#*l!dZoGk+qX;fj zD1@8!Eo1SQ=-X5D7R5U#34(8#R0+m|G000&r5J(B6zUtxxory6sABx>m^uM6_M14} z!Zc+2P*`r#-;1{qa9XvENlwniR$z?VB!pJvP=<9gAVKPH%fO(uS>`t*PpY~s_M|y$ zWI*{3f>d-9`YzPZ51{gvQVOMg1hwp&)2TVV&*BgEWhJZcM^Rp?SYhA!9$goc_59C} z)`ZQa#ZXJe)wj%~OYfFY`Jw!H$JUQh#u!+3VyqbO`5NH!F941fSje!-n9tbgBFT>; zg&As2@V#L_=XrGk7$q=Ti5n#oEQA@EV_V#63m}EPM1+$}vHlF2)!3M3L^qX-1Rucw z9H8-W(76f2l3jx5HWb_p1lsY%hRezc6keFbVQ?#lTFh+dd_p!}KrLYJLUBXTm|?(# zn0sWEkh0lrG#flw^I1Lt))jaO*$wV(VUmnfpP#7!4c&b<2YLV`U==wOt6(lN|klJ(NJ4Lu+Acdi9G8+ z3~Zbi3>g39R_j(Y5dVN?cAVE;IHVkp5{w4!|L|HO9nr_{2) z>8O5()Rx@P#jQMh`J1>DJ-`N2&;qj!@Fm_RB?-%|9vXuzVJpoRs%uDX$n{1kEGRJC zt9%HiGNN@|6;rLi%ByIiiOrVWyi>J1f!GVUpRsOk8Vk!D3X_q6E&F|0G@O)vh7*4L z#fzKMcClMQ1pjEsakU1Ti#nAUyIDWJ)iXHz8JYJ9#4raV| z!hy*D!H%3kSGr?k2}!6A&ExpAjeNKj@FSN`4)z+3@{!{QD8HJ4*}8qJl-$nn>?*&U zaVT}%HM@CeH`SKbRyAm23iGPQZ-9E8384-<()Bv?1m@a=9+2;KC3bh#=J#cArN#z9 z3DLubU2V_=vO?-z#zI%yb@f}PcE%`0ixa%r1mHfS<~=5idFLxRRc^|ys`^uili1q^ z|7Re-{R{bD=UY2=2`H=jPl2-Dcogc;>*UOj`HZ_-Xz3DC?M gEDC47a?U3XZ~$ju!m+ME=n5ng3uGQae#zJS9||qdNdN!< literal 0 HcmV?d00001 diff --git a/test/test_lz4.xisf b/test/test_lz4.xisf new file mode 100644 index 0000000000000000000000000000000000000000..027c9e7b92cd28cde303b5a2b23836e8770ff94f GIT binary patch literal 6134 zcmeHL$#&vM7VUNZKyRb9Xk4cgp@F%8Iw2vjT+EUyaqeUh2DLSCBw@fG+}}|z1V{lb zAUjvRC@l;|5jWm=--|cbjh-M1!hin$x8!O4w)0%{LBhxh{5B`@0*8oi2Mfnvwz+4s zSFCftbz1)@7Qbk_nD~+N>Jp@T){-EDgpTDppH>WeQ7;HRD@H{$ha*mi2mrsRA+chz zKwBqXp^3B80cE+0Q53wzTPq~6XBEfmW~sDxcFy4?ZwFrKOR?B#?VRZC1a}k=ME0XL zcignyZuyIibPtD;WzAjWoGsnTd=M^6k|2ns`EX#^E8R8z> zdn(3QS-$u2RPQUxse-5U-BO3&AMy5GQ}O$wvb6hn3k#Q6dWH}Bv`SRoPOGPz#_wdm z4Z;QTEe}*Q^xZUNP#S`OFIU*7x&)QSN z$Cl$Ga-f00zoMWz9uBn;KpkacBug+KdM?>9EMkV1AFYEhM#eshiT4Yl{skrh?HA7Z2<1zna<-0Z;r|Qa5t;#wzczdH3Ogsvj!ch>p@e1?`NY&2(}=A9 z%^^e97YK2+#|mwPF(`U6>2g(+4J-YBMkjeOQF!n*9A^C{_b) zlp4A&oCSCf`d1eX4vo%(=UvGXJTZZAQxGV}X9<_m2@o$d($KhFhnkH|sC-N`oBY$}gm|4r-|JwbvAXohJ5JO-QaO28&kcQrK)*h0O5DiRW_ ze5HC77B*1aoAzKtrG}PekqxyRDQ}{&BGwyxy(UUE#;|8_KO)^k*Jg&^WjZ4b8|Eb~ zYE~3~g$sJ{PSfBUiLO*4ZThT_##tjxZO=$|dYv1U(8Oy+Xmv6VNP;961+geqOtIRO zs!gFvkv{6``OtmLTt6CTWS6`G zjb|i+i&?^*a3OL&>6O!Mb2ZwQkT79Y`2-rb7AC5Z#o=`_iH-4i$kD2u5&>)^`>0=-YT6i zuA&7YUawuqZ<_m;m~f9djf_RU&NqtU4