improved perfs and event management

This commit is contained in:
Martino Ferrari committed 2026-10-13 23:00:42 +02:00
1 parent 667ed0c100
commit bb99be5889
17 files changed
+626 -59

No files matched your search

+39 -5
View File
@@ -13,6 +13,7 @@
#include <ctime>
#include "layout_store.hpp"
#include "waveform.hpp"
namespace udpscope {
namespace {
@@ -52,25 +53,30 @@ std::string stem_of(const std::string& file, const char* ext) {
/// The palette is fixed rather than the plots': this is navigation, and what has to be told apart at
/// a glance is one *event* from another, not one signal from another (for which the names are on the
/// row above).
bool write_preview(const ExportTable& table, const std::string& path, int width = 480, int height = 160) {
///
/// The height is **per strip**, not for the whole picture: with a fixed total, a six-signal event got
/// 26 pixels a signal and an eight-signal one got 20, which is a smear and not a waveform -- the
/// picture has to grow with the signal count, and it is the *viewer* that scales it down to a row.
bool write_preview(const ExportTable& table, const std::string& path, int width = 480,
int strip_h = 44) {
if (table.empty() || table.columns.empty()) return false;
const std::size_t rows = table.rows();
const std::size_t strips = table.columns.size();
const int strip_h = std::max(6, height / static_cast<int>(strips));
const int total_h = strip_h * static_cast<int>(strips);
static const std::uint32_t kPalette[] = {0x4FA3FFu, 0xFF7A4Fu, 0x6EE07Au, 0xE06ED0u,
0xFFD24Fu, 0x6FD8E0u, 0xB9A0FFu, 0xE0E0E0u};
// One byte per pixel per channel, bottom-up (BMP's own order), padded to a 4-byte row.
// One byte per pixel per channel, bottom-up and **blue first** -- both are BMP's own order, and
// writing RGB here is what made every external viewer show the traces in the wrong colour.
const int row_bytes = ((width * 3) + 3) & ~3;
std::vector<unsigned char> pixels(std::size_t(row_bytes) * total_h, 0);
const auto put = [&](int x, int y, std::uint32_t rgb, double alpha) {
if (x < 0 || y < 0 || x >= width || y >= total_h) return;
unsigned char* p = &pixels[std::size_t(total_h - 1 - y) * row_bytes + std::size_t(x) * 3];
const double a = std::clamp(alpha, 0.0, 1.0);
p[0] = static_cast<unsigned char>(p[0] * (1.0 - a) + ((rgb >> 16) & 0xFF) * a);
p[0] = static_cast<unsigned char>(p[0] * (1.0 - a) + (rgb & 0xFF) * a);
p[1] = static_cast<unsigned char>(p[1] * (1.0 - a) + ((rgb >> 8) & 0xFF) * a);
p[2] = static_cast<unsigned char>(p[2] * (1.0 - a) + (rgb & 0xFF) * a);
p[2] = static_cast<unsigned char>(p[2] * (1.0 - a) + ((rgb >> 16) & 0xFF) * a);
};
// The background: a dark field, so the traces read the way they do on screen.
for (int y = 0; y < total_h; ++y) {
@@ -268,6 +274,34 @@ std::string EventStore::save(const ExportTable& table, const EventRecord& meta,
return samples;
}
std::string EventStore::redraw_preview(const EventRecord& record) const {
if (dir_.empty() || record.stem.empty() || record.file.empty()) return {};
WaveformReader reader;
std::string error;
if (!reader.open(record.file, &error)) return {};
// The whole event in one table, which an event can afford where an export cannot: it is bounded by
// the trigger's own pre/post window, so this is seconds of samples and not a session's worth.
ExportTable table;
table.columns.resize(reader.names().size());
for (std::size_t c = 0; c < table.columns.size(); ++c) {
table.columns[c].name = reader.names()[c];
table.columns[c].values.reserve(reader.rows());
}
table.time.reserve(reader.rows());
std::vector<float> values(reader.names().size(), 0.0f);
double t = 0.0;
double abs_t = 0.0;
while (reader.next(&t, &abs_t, values.data())) {
table.time.push_back(t);
for (std::size_t c = 0; c < table.columns.size(); ++c) {
table.columns[c].values.push_back(values[c]);
}
}
const std::string path = dir_ + "/" + record.stem + ".bmp";
if (!write_preview(table, path)) return {};
return path;
}
std::vector<EventRecord> EventStore::list() const {
std::vector<EventRecord> out;
if (dir_.empty()) return out;