diff --git a/plugins/visualizers/CMakeLists.txt b/plugins/visualizers/CMakeLists.txt index d92efde9d8298..216af43d85b55 100644 --- a/plugins/visualizers/CMakeLists.txt +++ b/plugins/visualizers/CMakeLists.txt @@ -26,6 +26,7 @@ add_imhex_plugin( source/content/pl_visualizers/coordinates.cpp source/content/pl_visualizers/timestamp.cpp source/content/pl_visualizers/table.cpp + source/content/pl_visualizers/digital_signal.cpp INCLUDES include ${MINIAUDIO_INCLUDE_DIRS} diff --git a/plugins/visualizers/source/content/pl_visualizers.cpp b/plugins/visualizers/source/content/pl_visualizers.cpp index 32b9502d68e07..d25bf66c4a881 100644 --- a/plugins/visualizers/source/content/pl_visualizers.cpp +++ b/plugins/visualizers/source/content/pl_visualizers.cpp @@ -14,6 +14,7 @@ namespace hex::plugin::visualizers { void drawCoordinateVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); void drawTimestampVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); void drawTableVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); + void drawDigitalSignalVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); void registerPatternLanguageVisualizers() { using ParamCount = pl::api::FunctionParameterCount; @@ -27,6 +28,7 @@ namespace hex::plugin::visualizers { ContentRegistry::PatternLanguage::addVisualizer("coordinates", drawCoordinateVisualizer, ParamCount::exactly(2)); ContentRegistry::PatternLanguage::addVisualizer("timestamp", drawTimestampVisualizer, ParamCount::exactly(1)); ContentRegistry::PatternLanguage::addVisualizer("table", drawTableVisualizer, ParamCount::exactly(3)); + ContentRegistry::PatternLanguage::addVisualizer("digital_signal", drawDigitalSignalVisualizer, ParamCount::exactly(1)); } } diff --git a/plugins/visualizers/source/content/pl_visualizers/digital_signal.cpp b/plugins/visualizers/source/content/pl_visualizers/digital_signal.cpp new file mode 100644 index 0000000000000..6d4597f19dabf --- /dev/null +++ b/plugins/visualizers/source/content/pl_visualizers/digital_signal.cpp @@ -0,0 +1,100 @@ +#include + +#include + +#include +#include +#include + +#include +#include + +namespace hex::plugin::visualizers { + + void drawDigitalSignalVisualizer(pl::ptrn::Pattern &pattern, pl::ptrn::IIterable &iterable, bool shouldReset, std::span) { + auto *bitfield = dynamic_cast(&pattern); + if (bitfield == nullptr) + throw std::logic_error("Digital signal visualizer only works with bitfields."); + + + struct DataPoint { + std::array points; + std::string label; + std::string value; + ImColor color; + }; + static std::vector dataPoints; + static ImVec2 lastPoint; + + if (shouldReset) { + dataPoints.clear(); + lastPoint = { 0, 0 }; + + iterable.forEachEntry(0, iterable.getEntryCount(), [&](u64, pl::ptrn::Pattern *entry) { + size_t bitSize = 0; + if (auto bitfieldField = dynamic_cast(entry); bitfieldField != nullptr) + bitSize = bitfieldField->getBitSize(); + else + bitSize = entry->getSize() * 8; + + auto value = entry->getValue(); + bool high = value.toUnsigned() > 0; + dataPoints.emplace_back( + std::array { lastPoint, { lastPoint.x, high ? 1.0F : 0.0F } }, + entry->getDisplayName(), + entry->getFormattedValue(), + entry->getColor() + ); + + lastPoint = dataPoints.back().points[1]; + lastPoint.x += bitSize; + }); + + dataPoints.push_back({ + .points = { lastPoint, { lastPoint.x, 0 } }, + .label = "", + .value = "", + .color = ImColor(0x00) + }); + } + + if (ImPlot::BeginPlot("##Signal", ImVec2(600_scaled, 200_scaled), ImPlotFlags_NoLegend | ImPlotFlags_NoFrame | ImPlotFlags_NoMenus | ImPlotFlags_NoMouseText)) { + ImPlot::SetupAxisLimitsConstraints(ImAxis_X1, 0, lastPoint.x); + + ImPlot::SetupAxis(ImAxis_Y1, "", ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax); + ImPlot::SetupAxisFormat(ImAxis_Y1, ""); + ImPlot::SetupAxisLimits(ImAxis_Y1, -0.1F, 1.1F); + + for (size_t i = 0; i < dataPoints.size() - 1; i += 1) { + const auto &left = dataPoints[i]; + const auto &right = dataPoints[i + 1]; + + { + auto x = left.points[1].x + (right.points[0].x - left.points[1].x) / 2; + ImPlot::Annotation(x, 0.55F, left.color, {}, false, "%s", left.label.c_str()); + ImPlot::Annotation(x, 0.40F, left.color, {}, false, "%s", left.value.c_str()); + } + + { + ImVec2 min = ImPlot::PlotToPixels(ImPlotPoint(left.points[0].x, 0)); + ImVec2 max = ImPlot::PlotToPixels(ImPlotPoint(right.points[1].x, 1)); + + ImPlot::PushPlotClipRect(); + auto transparentColor = left.color; + transparentColor.Value.w = 0.4F; + ImPlot::GetPlotDrawList()->AddRectFilled(min, max, transparentColor); + ImPlot::PopPlotClipRect(); + } + } + + ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 2_scaled); + ImPlot::PlotLineG("Signal", [](int idx, void*) -> ImPlotPoint { + return dataPoints[idx / 2].points[idx % 2]; + }, nullptr, dataPoints.size() * 2); + ImPlot::PopStyleVar(); + + ImPlot::EndPlot(); + } + } + +}