diff --git a/libxisf.cpp b/libxisf.cpp index 7f64041..ae84920 100644 --- a/libxisf.cpp +++ b/libxisf.cpp @@ -38,6 +38,43 @@ template<> struct std::hash } }; #endif +namespace LibXISF +{ +class MatrixConvert +{ + int _rows = 0; + int _cols = 0; + QByteArray _data; +public: + const QByteArray& data() const + { + return _data; + } + int rows() const { return _rows; } + int cols() const { return _cols; } + MatrixConvert() = default; + MatrixConvert(int rows, int cols, const QByteArray &data) : _rows(rows), _cols(cols), _data(data) {} + + template + static Matrix toMatrix(const MatrixConvert &m) + { + Matrix matrix(m._rows, m._cols); + std::memcpy(&matrix._elem[0], m._data.constData(), m._data.size()); + return matrix; + } + template + static MatrixConvert fromMatrix(const Matrix &m) + { + MatrixConvert mc; + mc._rows = m._rows; + mc._cols = m._cols; + mc._data = QByteArray((const char*)&m._elem[0], mc._rows * mc._cols * sizeof(T)); + return mc; + } +}; +} + +Q_DECLARE_METATYPE(LibXISF::MatrixConvert); namespace LibXISF { @@ -51,6 +88,7 @@ static std::unordered_map sampleFormatToString; static std::unordered_map colorSpaceToEnum; static std::unordered_map colorSpaceToString; static std::unordered_map vectorTypeSizes; +static std::unordered_map matrixTypeSizes; static void byteShuffle(QByteArray &data, int itemSize) { @@ -594,6 +632,7 @@ Property XISFReader::readPropertyElement() if(typeToId.count(type) == 0) throw Error("Invalid type in property"); + int typeId = typeToId[type]; QVariant value; if(type == "String" && !attributes.hasAttribute("location")) @@ -603,7 +642,7 @@ Property XISFReader::readPropertyElement() else if(attributes.hasAttribute("value")) { value = attributes.value("value").toString(); - value.convert(typeToId[type]); + value.convert(typeId); property.value = value; } else @@ -614,9 +653,28 @@ Property XISFReader::readPropertyElement() _io->seek(dataBlock.attachmentPos); dataBlock.decompress(_io->read(dataBlock.attachmentSize)); } - property.value = dataBlock.data; - if(!property.value.convert(typeToId[type])) - throw Error("Failed to convert property data"); + if(vectorTypeSizes.count(typeId)) + { + property.value = dataBlock.data; + if(!property.value.convert(typeId)) + throw Error("Failed to convert vector property data"); + } + else if(matrixTypeSizes.count(typeId)) + { + bool ok1, ok2; + int rows = attributes.value("rows").toInt(&ok1); + int cols = attributes.value("columns").toInt(&ok2); + if(!ok1 || !ok2) + throw Error("Invalid rows and/or columns"); + + property.value = QVariant::fromValue(MatrixConvert(rows, cols, dataBlock.data)); + if(!property.value.convert(typeId)) + throw Error("Failed to convert matrix property data"); + } + else + { + property.value = dataBlock.data; + } } return property; @@ -886,6 +944,16 @@ void XISFWriter::writePropertyElement(const Property &property) _xml->writeAttribute("length", QString::number(dataBlock.data.size() / vectorTypeSizes[type])); _xml->writeCharacters(dataBlock.data.toBase64()); } + else if(matrixTypeSizes.count(type)) + { + MatrixConvert mc = property.value.value(); + DataBlock dataBlock; + dataBlock.data = mc.data(); + writeDataBlockAttributes(dataBlock); + _xml->writeAttribute("rows", QString::number(mc.rows())); + _xml->writeAttribute("columns", QString::number(mc.cols())); + _xml->writeCharacters(dataBlock.data.toBase64()); + } else _xml->writeAttribute("value", property.value.toString()); _xml->writeEndElement(); @@ -925,6 +993,10 @@ void XISFWriter::writeMetadata() return t; \ }); } +#define REGISTER_MATRIX_TYPE(type, mtype) { matrixTypeSizes.insert({typeToId[#type], sizeof(mtype)}); \ + QMetaType::registerConverter(MatrixConvert::fromMatrix); \ + QMetaType::registerConverter(MatrixConvert::toMatrix);} + struct Init { Init() @@ -1010,6 +1082,19 @@ struct Init REGISTER_VECTOR_TYPE(C32Vector); REGISTER_VECTOR_TYPE(C64Vector); + REGISTER_MATRIX_TYPE(I8Matrix, Int8); + REGISTER_MATRIX_TYPE(UI8Matrix, UInt8); + REGISTER_MATRIX_TYPE(I16Matrix, Int16); + REGISTER_MATRIX_TYPE(UI16Matrix, UInt16); + REGISTER_MATRIX_TYPE(I32Matrix, Int32); + REGISTER_MATRIX_TYPE(UI32Matrix, UInt32); + REGISTER_MATRIX_TYPE(I64Matrix, Int64); + REGISTER_MATRIX_TYPE(UI64Matrix, UInt64); + REGISTER_MATRIX_TYPE(F32Matrix, Float32); + REGISTER_MATRIX_TYPE(F64Matrix, Float64); + REGISTER_MATRIX_TYPE(C32Matrix, Complex32); + REGISTER_MATRIX_TYPE(C64Matrix, Complex64); + 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) diff --git a/libxisf.h b/libxisf.h index 13d2cac..726f8ee 100644 --- a/libxisf.h +++ b/libxisf.h @@ -251,9 +251,16 @@ struct Complex64 template class Matrix { - int width; - int height; - std::vector elem; + int _rows = 0; + int _cols = 0; + std::vector _elem; +public: + Matrix() = default; + Matrix(int rows, int cols) : _rows(rows), _cols(cols), _elem(rows * cols) {} + void resize(int rows, int cols) { _rows = rows; _cols = cols; _elem.resize(rows * cols); } + T& operator()(int row, int col) { return _elem[row * _cols + col]; } + + friend class MatrixConvert; }; typedef bool Boolean; diff --git a/test/main.cpp b/test/main.cpp index 2dbdaed..e5a736a 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -48,6 +48,12 @@ int main(int argc, char **argv) image.addProperty(Property("PropertyComplex64", Complex64{-3.0, 2.0})); image.addProperty(Property("VectorUInt16", UI16Vector({23, 45, 86}))); image.addProperty(Property("VectorComplex32", C32Vector({{1, 2}, {3, 4}, {5, 6}}))); + UI16Matrix m(2, 3); + m(0, 0) = 0; + m(0, 1) = 1; + m(0, 2) = 2; + m(1, 0) = 10; + image.addProperty(Property("UI16Matrix", m)); image.addFITSKeyword({"RA", "226.9751163116387", "Right ascension of the center of the image (deg)"}); image.addFITSKeyword({"DEC", "62.02302376908295", "Declination of the center of the image (deg)"}); writer.writeImage(image);