Skip to content

Commit 203d7af

Browse files
committed
Add gradient feature
1 parent 57d51b5 commit 203d7af

10 files changed

Lines changed: 164 additions & 67 deletions

File tree

.gitmodules

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
[submodule "external/ImGuiFileDialog"]
88
path = external/ImGuiFileDialog
99
url = https://github.com/aiekick/ImGuiFileDialog
10+
1011
[submodule "external/imgui_gradient"]
1112
path = external/imgui_gradient
12-
url = https://github.com/Coollab-Art/imgui_gradient
13+
url = https://github.com/maede97/imgui_gradient

CMakeLists.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ target_include_directories(glad PUBLIC external/glad/include)
3232
# --- ImGuiFileDialog ---
3333
add_library(imguifiledialog STATIC external/ImGuiFileDialog/ImGuiFileDialog.cpp)
3434
target_include_directories(imguifiledialog PUBLIC external/ImGuiFileDialog external/imgui)
35-
target_link_libraries(imgui PUBLIC imguifiledialog)
35+
target_link_libraries(imguifiledialog PUBLIC imgui)
36+
37+
# --- ImGui_Gradient ---
38+
add_subdirectory(external/imgui_gradient)
39+
target_include_directories(imgui_gradient SYSTEM PRIVATE external/imgui)
40+
target_compile_definitions(imgui_gradient PRIVATE IMGUI_DEFINE_MATH_OPERATORS)
3641

3742
# --- Core library ---
3843
add_library(core STATIC src/core.cpp)
@@ -49,8 +54,8 @@ target_include_directories(hex_display_feature_manager PUBLIC include external/i
4954

5055
# --- UI library ---
5156
add_library(ui STATIC src/ui.cpp)
52-
target_include_directories(ui PUBLIC include external/imgui external/ImGuiFileDialog)
53-
target_link_libraries(ui PUBLIC core imgui glad glfw imguifiledialog)
57+
target_include_directories(ui PUBLIC include external/imgui external/ImGuiFileDialog external/imgui_gradient)
58+
target_link_libraries(ui PUBLIC core imgui glad glfw imguifiledialog imgui_gradient)
5459

5560
# --- App library ---
5661
add_library(app STATIC src/app.cpp)

external/imgui_gradient

include/entropy/app.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
#pragma once
2-
#include <entropy/ui.h>
2+
#define IMGUI_DEFINE_MATH_OPERATORS
33

4-
#include <atomic>
54
#include <entropy/core.h>
65
#include <entropy/hex_display_feature_manager.h>
6+
#include <entropy/ui.h>
7+
8+
#include <atomic>
79
#include <filesystem>
810
#include <fstream>
911
#include <functional>
12+
#include <imgui_gradient/imgui_gradient.hpp>
1013
#include <map>
1114
#include <memory>
1215
#include <string>
@@ -28,6 +31,7 @@ struct UiState {
2831
bool showHelp = false;
2932
bool showSearchWindow = false;
3033
bool showFeatureSettings = false;
34+
bool showGeneralSettings = false;
3135
size_t highlighted_sector = SIZE_MAX;
3236
std::vector<uint8_t> currentSectorData;
3337
size_t currentSectorIndex = 0;
@@ -74,6 +78,9 @@ struct AppState {
7478
// Hex Display Features
7579
std::unique_ptr<HexDisplayFeatureManager> hexDisplayFeatureManager;
7680
std::map<std::string, bool> featureEnabled;
81+
ImGG::GradientWidget gradient_widget;
82+
83+
void resetHexDisplayGradientColors();
7784
};
7885

7986
int parseCommandLine(int argc, char **argv, AppState &state);

include/entropy/core.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,5 @@ double unpack_value(uint8_t packed);
5454
uint8_t pack_value(double value);
5555
template <typename T> T clip(const T &n, const T &lower, const T &upper) { return std::max(lower, std::min(n, upper)); }
5656
double shannon_entropy(const unsigned char *data, size_t size);
57-
void value_to_color(float v, uint8_t &r, uint8_t &g, uint8_t &b);
5857

5958
} // namespace entropy

include/entropy/ui.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#define IMGUI_DEFINE_MATH_OPERATORS
4+
35
#include <atomic>
46
#include <functional>
57
#include <string>
@@ -26,6 +28,6 @@ void handleFileDialogs(UiState &uiState, AppState &appState, IGFD::FileDialogCon
2628
void renderVisualization(ImDrawList *draw_list, GLuint tex, const std::vector<uint8_t> &block_buffer, float zoom, ImVec2 pan_offset,
2729
size_t current_block, size_t block_size, size_t block_width, size_t block_height, UiState &uiState,
2830
std::function<void(size_t)> loadHexData);
29-
void upload_block(GLuint tex, const std::vector<uint8_t> &raw, size_t block_width, size_t block_height);
31+
void upload_block(GLuint tex, const std::vector<uint8_t> &raw, size_t block_width, size_t block_height, const AppState &appState);
3032

3133
} // namespace entropy

main.cpp

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#define IMGUI_DEFINE_MATH_OPERATORS
2+
13
#define GLFW_INCLUDE_NONE
24
#include <GLFW/glfw3.h>
35
#include <algorithm>
@@ -55,6 +57,9 @@ int main(int argc, char **argv) {
5557
// return string "Enabled" as a void* (which is later used to infer the section)
5658
return (void *)"Enabled";
5759

60+
if (strcmp(name, "Gradient") == 0)
61+
return (void *)"Gradient";
62+
5863
// check if name matches any feature slug
5964
for (const auto *f : globalAppState->hexDisplayFeatureManager->getFeatures()) {
6065
std::string slug = f->getSlug();
@@ -66,39 +71,83 @@ int main(int argc, char **argv) {
6671
return nullptr;
6772
};
6873
featuresHandler.ReadLineFn = [](ImGuiContext *, ImGuiSettingsHandler *, void *entry, const char *line) {
69-
if (entry != (void *)"Enabled") {
70-
auto *feature = static_cast<entropy::HexDisplayFeature *>(entry);
74+
if (entry == (void *)"Enabled") {
7175
std::string line_str = line;
7276
size_t eq_pos = line_str.find('=');
7377
if (eq_pos != std::string::npos) {
74-
std::string key = line_str.substr(0, eq_pos);
78+
std::string slug = line_str.substr(0, eq_pos);
7579
std::string value = line_str.substr(eq_pos + 1);
7680

77-
feature->setConfig(key, value);
81+
std::string featureName;
82+
// Find feature by slug
83+
for (const auto *f : globalAppState->hexDisplayFeatureManager->getFeatures()) {
84+
std::string f_slug = f->getSlug();
85+
if (f_slug == slug) {
86+
featureName = f->getName();
87+
break;
88+
}
89+
}
90+
if (featureName.empty())
91+
return; // Unknown feature
92+
93+
globalAppState->featureEnabled[featureName] = (value == "1");
7894
}
95+
}
7996

80-
return;
97+
if (entry == (void *)"Gradient") {
98+
std::string line_str = line;
99+
size_t eq_pos = line_str.find('=');
100+
if (eq_pos != std::string::npos) {
101+
std::string key = line_str.substr(0, eq_pos);
102+
std::string value = line_str.substr(eq_pos + 1);
103+
104+
if (key == "marks") {
105+
// Parse marks
106+
std::list<ImGG::Mark> marks;
107+
size_t start = 0;
108+
while (start < value.length()) {
109+
size_t comma_pos = value.find(',', start);
110+
std::string mark_str = (comma_pos == std::string::npos) ? value.substr(start) : value.substr(start, comma_pos - start);
111+
112+
size_t colon_pos = mark_str.find(':');
113+
if (colon_pos != std::string::npos) {
114+
float pos = std::stof(mark_str.substr(0, colon_pos));
115+
std::string color_str = mark_str.substr(colon_pos + 1);
116+
unsigned int color_value = 0;
117+
if (sscanf(color_str.c_str(), "0x%X", &color_value) == 1) {
118+
ImGG::Mark mark;
119+
mark.position = ImGG::RelativePosition{pos};
120+
mark.color = ImColor(color_value);
121+
marks.push_back(mark);
122+
}
123+
}
124+
125+
if (comma_pos == std::string::npos)
126+
break;
127+
start = comma_pos + 1;
128+
}
129+
130+
globalAppState->gradient_widget.gradient() = ImGG::Gradient{marks};
131+
}
132+
133+
if (key == "interpolation") {
134+
int mode = std::stoi(value);
135+
globalAppState->gradient_widget.gradient().interpolation_mode() = static_cast<ImGG::Interpolation>(mode);
136+
}
137+
138+
return;
139+
}
81140
}
82141

142+
// Settings for each hex display feature
143+
auto *feature = static_cast<entropy::HexDisplayFeature *>(entry);
83144
std::string line_str = line;
84145
size_t eq_pos = line_str.find('=');
85146
if (eq_pos != std::string::npos) {
86-
std::string slug = line_str.substr(0, eq_pos);
147+
std::string key = line_str.substr(0, eq_pos);
87148
std::string value = line_str.substr(eq_pos + 1);
88149

89-
std::string featureName;
90-
// Find feature by slug
91-
for (const auto *f : globalAppState->hexDisplayFeatureManager->getFeatures()) {
92-
std::string f_slug = f->getSlug();
93-
if (f_slug == slug) {
94-
featureName = f->getName();
95-
break;
96-
}
97-
}
98-
if (featureName.empty())
99-
return; // Unknown feature
100-
101-
globalAppState->featureEnabled[featureName] = (value == "1");
150+
feature->setConfig(key, value);
102151
}
103152
};
104153
featuresHandler.WriteAllFn = [](ImGuiContext *, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf) {
@@ -110,6 +159,23 @@ int main(int argc, char **argv) {
110159
}
111160
buf->appendf("\n");
112161

162+
// Gradient settings
163+
buf->appendf("[%s][Gradient]\n", handler->TypeName);
164+
const auto &gradient = globalAppState->gradient_widget.gradient();
165+
buf->appendf("marks=");
166+
bool first = true;
167+
for (const auto &mark : gradient.get_marks()) {
168+
if (!first) {
169+
buf->appendf(",");
170+
}
171+
first = false;
172+
unsigned int color_value = ImGui::ColorConvertFloat4ToU32(mark.color);
173+
buf->appendf("%.6f:0x%08X", mark.position.get(), color_value);
174+
}
175+
buf->appendf("\n");
176+
buf->appendf("interpolation=%d\n", static_cast<int>(gradient.interpolation_mode()));
177+
buf->appendf("\n");
178+
113179
// config for each feature
114180
for (const auto *f : globalAppState->hexDisplayFeatureManager->getFeatures()) {
115181
std::string slug = f->getSlug();
@@ -129,6 +195,8 @@ int main(int argc, char **argv) {
129195

130196
ImGui::AddSettingsHandler(&featuresHandler);
131197

198+
appState.resetHexDisplayGradientColors();
199+
132200
// Load settings
133201
ImGui::LoadIniSettingsFromDisk(ImGui::GetIO().IniFilename);
134202

src/app.cpp

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ namespace entropy {
1010

1111
#define ALL_FILES_FILTER "All files {((.*))}"
1212

13+
void AppState::resetHexDisplayGradientColors() {
14+
gradient_widget.gradient().clear();
15+
gradient_widget.gradient().add_mark(ImGG::Mark{ImGG::RelativePosition{0.0f}, ImVec4(0.0f, 0.0f, 1.0f, 1.0f)});
16+
gradient_widget.gradient().add_mark(ImGG::Mark{ImGG::RelativePosition{0.25f}, ImVec4(0.0f, 1.0f, 1.0f, 1.0f)});
17+
gradient_widget.gradient().add_mark(ImGG::Mark{ImGG::RelativePosition{0.5f}, ImVec4(0.0f, 1.0f, 0.0f, 1.0f)});
18+
gradient_widget.gradient().add_mark(ImGG::Mark{ImGG::RelativePosition{0.75f}, ImVec4(1.0f, 1.0f, 0.0f, 1.0f)});
19+
gradient_widget.gradient().add_mark(ImGG::Mark{ImGG::RelativePosition{1.0f}, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)});
20+
21+
gradient_widget.gradient().interpolation_mode() = ImGG::Interpolation::Linear;
22+
}
23+
1324
int parseCommandLine(int argc, char **argv, AppState &state) {
1425
state.originalFile = ""; // Reset
1526
if (argc == 3) {
@@ -324,11 +335,11 @@ void mainLoop(GLFWwindow *window, GLuint tex, AppState &state, UiState &uiState,
324335

325336
if (state.redrawBlock) {
326337
if (load_block_from_file(state.file, state.current_block, block_size, state.file_size, state.block_buffer)) {
327-
upload_block(tex, state.block_buffer, state.block_width, state.block_height);
338+
upload_block(tex, state.block_buffer, state.block_width, state.block_height, state);
328339
} else {
329340
// Clear visualization if load fails
330341
state.block_buffer.assign(0, 0);
331-
upload_block(tex, state.block_buffer, state.block_width, state.block_height);
342+
upload_block(tex, state.block_buffer, state.block_width, state.block_height, state);
332343
}
333344
state.redrawBlock = false;
334345
}
@@ -360,13 +371,22 @@ void mainLoop(GLFWwindow *window, GLuint tex, AppState &state, UiState &uiState,
360371
}
361372
ImGui::EndMenu();
362373
}
363-
if (ImGui::BeginMenu("Hex Display Features")) {
364-
for (const auto *feature : state.hexDisplayFeatureManager->getFeatures()) {
365-
ImGui::MenuItem(feature->getName().c_str(), NULL, &state.featureEnabled.at(feature->getName()));
374+
if (ImGui::BeginMenu("Settings")) {
375+
if (ImGui::BeginMenu("Enabled Features")) {
376+
for (const auto *feature : state.hexDisplayFeatureManager->getFeatures()) {
377+
ImGui::MenuItem(feature->getName().c_str(), NULL, &state.featureEnabled.at(feature->getName()));
378+
}
379+
ImGui::EndMenu();
366380
}
367-
if (ImGui::MenuItem("Settings")) {
381+
382+
if (ImGui::MenuItem("Feature Settings")) {
368383
uiState.showFeatureSettings = true;
369384
}
385+
386+
if (ImGui::MenuItem("General Settings")) {
387+
uiState.showGeneralSettings = true;
388+
}
389+
370390
ImGui::EndMenu();
371391
}
372392
}
@@ -503,6 +523,25 @@ void mainLoop(GLFWwindow *window, GLuint tex, AppState &state, UiState &uiState,
503523
ImGui::End();
504524
}
505525

526+
if (uiState.showGeneralSettings) {
527+
ImGui::Begin("General Settings", &uiState.showGeneralSettings);
528+
529+
ImGG::Settings settings;
530+
settings.flags = ImGG::Flag::NoResetButton;
531+
settings.gradient_width = 5000; // arbitrary large to use full width
532+
if (state.gradient_widget.widget("Hex Color Gradient", settings)) {
533+
state.redrawBlock = true;
534+
}
535+
if (ImGG::interpolation_mode_widget("Interpolation Mode", &state.gradient_widget.gradient().interpolation_mode())) {
536+
state.redrawBlock = true;
537+
}
538+
if (ImGui::Button("Reset Gradient to Default")) {
539+
state.resetHexDisplayGradientColors();
540+
state.redrawBlock = true;
541+
}
542+
ImGui::End();
543+
}
544+
506545
// Visualization
507546
ImDrawList *draw_list = ImGui::GetBackgroundDrawList();
508547
renderVisualization(draw_list, tex, state.block_buffer, state.zoom, state.pan_offset, state.current_block, block_size, state.block_width,

src/core.cpp

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -35,29 +35,4 @@ double shannon_entropy(const unsigned char *data, size_t size) {
3535
return entropy;
3636
}
3737

38-
// Map 0-1 value to color (blue -> green -> yellow -> red)
39-
void value_to_color(float v, uint8_t &r, uint8_t &g, uint8_t &b) {
40-
if (v < 0.25f) {
41-
float t = v / 0.25f;
42-
r = 0;
43-
g = uint8_t(t * 255);
44-
b = 255;
45-
} else if (v < 0.5f) {
46-
float t = (v - 0.25f) / 0.25f;
47-
r = 0;
48-
g = 255;
49-
b = uint8_t((1.0f - t) * 255);
50-
} else if (v < 0.75f) {
51-
float t = (v - 0.5f) / 0.25f;
52-
r = uint8_t(t * 255);
53-
g = 255;
54-
b = 0;
55-
} else {
56-
float t = (v - 0.75f) / 0.25f;
57-
r = 255;
58-
g = uint8_t((1.0f - t) * 255);
59-
b = 0;
60-
}
61-
}
62-
6338
} // namespace entropy

0 commit comments

Comments
 (0)