// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #pragma once #include #include #include "JFJochReader.h" #include "SMV.h" // Reads a rotation sweep straight from a directory of SMV files - what ADSC Quantum detectors // wrote and what Rayonix and others still write, so most archived CCD data from the 2000s is in // this format. // // The same shape as JFJochMarCCDReader, which it is otherwise a sibling of: the sweep's geometry // comes from the first file's header, the rotation angle of each image from its own, and images // are decoded on demand. A raw SMV file carries no analysis results, so no spots and no // reflections. // // SMV states no saturation value and marks no untrusted pixels, so nothing is masked and no pixel // is called an overload from the file alone - unlike marCCD, which at least carries a saturated // value. The beam centre it states is in millimetres and its convention varies between writers; // see the note in smv::Header. class JFJochSMVReader : public JFJochReader { std::vector files_; std::shared_ptr dataset_; smv::Header header0_; bool LoadImage_i(std::shared_ptr &dataset, DataMessage &message, std::vector &buffer, int64_t image_number, bool update_dataset) override; // Decodes one image into the caller's byte buffer and returns the image that points at it. The // stored 16-bit strip is read through scratch, which the caller keeps between frames. template CompressedImage DecodeInto(int64_t image_number, Buffer &buffer, std::vector &scratch) const; public: ~JFJochSMVReader() override = default; // True if the path names something this reader can open: a marCCD file, or a directory holding // at least one. Cheap - it reads a few kB at most. static bool CanRead(const std::string &path); // path is a directory of marCCD frames, or one frame inside the sweep to take the whole sweep // from. void ReadFiles(const std::string &path); [[nodiscard]] uint64_t GetNumberOfImages() const override; void Close() override; // Whether the sweep really has an image at this point. A deposited series can be missing frames, // and they are kept as gaps so that every image keeps the spindle angle its own header states; // ReadRawImage hands such a slot back as "nothing to read". [[nodiscard]] bool HasImage(int64_t image_number) const; bool ReadRawImage(int64_t image_number, JFJochReaderRawImage &image) override; [[nodiscard]] std::vector ReadSpots(int64_t image) const override; };