yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
color.cc
Go to the documentation of this file.
1#include "color.h"
2
3#include "absl/strings/str_format.h"
6#include "imgui/imgui.h"
7
8namespace yaze {
9namespace gui {
10
21 // SnesColor stores RGB as 0-255 in ImVec4, convert to standard 0-1 range
22 ImVec4 rgb_255 = color.rgb();
23 return ImVec4(rgb_255.x / 255.0f, rgb_255.y / 255.0f, rgb_255.z / 255.0f,
24 1.0f);
25}
26
34 // SnesColor constructor expects 0-1 range and handles conversion internally
35 return gfx::SnesColor(color);
36}
37
38IMGUI_API bool SnesColorButton(absl::string_view id, gfx::SnesColor& color,
39 ImGuiColorEditFlags flags,
40 const ImVec2& size_arg) {
41 // Convert the SNES color values to ImGui color values
42 ImVec4 displayColor = ConvertSnesColorToImVec4(color);
43
44 // Call the original ImGui::ColorButton with the converted color
45 bool pressed = ImGui::ColorButton(id.data(), displayColor, flags, size_arg);
46 // Add the SNES color representation to the tooltip
47 if (ImGui::IsItemHovered()) {
48 ImGui::BeginTooltip();
49 ImGui::Text("SNES: $%04X", color.snes());
50 ImGui::EndTooltip();
51 }
52 return pressed;
53}
54
55IMGUI_API bool SnesColorEdit4(absl::string_view label, gfx::SnesColor* color,
56 ImGuiColorEditFlags flags) {
57 // Convert from internal 0-255 storage to 0-1 for ImGui
58 ImVec4 displayColor = ConvertSnesColorToImVec4(*color);
59
60 // Call the original ImGui::ColorEdit4 with the converted color
61 bool changed =
62 ImGui::ColorEdit4(label.data(), (float*)&displayColor.x, flags);
63
64 // Only update if the user actually changed the color
65 if (changed) {
66 // set_rgb() handles conversion from 0-1 (ImGui) to 0-255 (internal)
67 // and automatically calculates snes_ value - no need to call set_snes
68 // separately
69 color->set_rgb(displayColor);
70 }
71
72 return changed;
73}
74
75// ============================================================================
76// New Standardized Palette Widgets
77// ============================================================================
78
79IMGUI_API bool InlinePaletteSelector(gfx::SnesPalette& palette, int num_colors,
80 int* selected_index) {
81 bool selection_made = false;
82 int colors_to_show = std::min(num_colors, static_cast<int>(palette.size()));
83
84 ImGui::BeginGroup();
85 for (int n = 0; n < colors_to_show; n++) {
86 ImGui::PushID(n);
87 if (n > 0 && (n % 8) != 0) {
88 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
89 }
90
91 bool is_selected = selected_index && (*selected_index == n);
92 if (is_selected) {
93 ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 1.0f, 0.0f, 1.0f));
94 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.0f);
95 }
96
98 "##palettesel", palette[n],
99 ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
100 ImVec2(20, 20))) {
101 if (selected_index) {
102 *selected_index = n;
103 selection_made = true;
104 }
105 }
106
107 if (is_selected) {
108 ImGui::PopStyleVar();
109 ImGui::PopStyleColor();
110 }
111
112 ImGui::PopID();
113 }
114 ImGui::EndGroup();
115
116 return selection_made;
117}
118
119IMGUI_API absl::Status InlinePaletteEditor(gfx::SnesPalette& palette,
120 const std::string& title,
121 ImGuiColorEditFlags flags) {
122 if (!title.empty()) {
123 ImGui::Text("%s", title.c_str());
124 }
125
126 static int selected_color = 0;
127 static ImVec4 current_color = ImVec4(0, 0, 0, 1.0f);
128
129 // Color picker
130 ImGui::Separator();
131 if (ImGui::ColorPicker4("##colorpicker", (float*)&current_color,
132 ImGuiColorEditFlags_NoSidePreview |
133 ImGuiColorEditFlags_NoSmallPreview)) {
134 gfx::SnesColor snes_color(current_color);
135 palette.UpdateColor(selected_color, snes_color);
136 }
137
138 ImGui::Separator();
139
140 // Palette grid
141 ImGui::BeginGroup();
142 for (int n = 0; n < palette.size(); n++) {
143 ImGui::PushID(n);
144 if ((n % 8) != 0) {
145 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
146 }
147
148 if (flags == 0) {
149 flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker;
150 }
151
152 if (SnesColorButton("##palettedit", palette[n], flags, ImVec2(20, 20))) {
153 selected_color = n;
154 current_color = ConvertSnesColorToImVec4(palette[n]);
155 }
156
157 // Context menu
158 if (ImGui::BeginPopupContextItem()) {
159 if (ImGui::MenuItem("Copy as SNES")) {
160 std::string clipboard = absl::StrFormat("$%04X", palette[n].snes());
161 ImGui::SetClipboardText(clipboard.c_str());
162 }
163 if (ImGui::MenuItem("Copy as RGB")) {
164 auto rgb = palette[n].rgb();
165 std::string clipboard =
166 absl::StrFormat("(%d,%d,%d)", (int)rgb.x, (int)rgb.y, (int)rgb.z);
167 ImGui::SetClipboardText(clipboard.c_str());
168 }
169 if (ImGui::MenuItem("Copy as Hex")) {
170 auto rgb = palette[n].rgb();
171 std::string clipboard = absl::StrFormat("#%02X%02X%02X", (int)rgb.x,
172 (int)rgb.y, (int)rgb.z);
173 ImGui::SetClipboardText(clipboard.c_str());
174 }
175 ImGui::EndPopup();
176 }
177
178 ImGui::PopID();
179 }
180 ImGui::EndGroup();
181
182 return absl::OkStatus();
183}
184
185IMGUI_API bool PopupPaletteEditor(const char* popup_id,
186 gfx::SnesPalette& palette,
187 ImGuiColorEditFlags flags) {
188 bool modified = false;
189
190 if (ImGui::BeginPopup(popup_id)) {
191 static int selected_color = 0;
192 static ImVec4 current_color = ImVec4(0, 0, 0, 1.0f);
193
194 // Compact color picker
195 if (ImGui::ColorPicker4("##popuppicker", (float*)&current_color,
196 ImGuiColorEditFlags_NoSidePreview |
197 ImGuiColorEditFlags_NoSmallPreview)) {
198 gfx::SnesColor snes_color(current_color);
199 palette.UpdateColor(selected_color, snes_color);
200 modified = true;
201 }
202
203 ImGui::Separator();
204
205 // Palette grid
206 ImGui::BeginGroup();
207 for (int n = 0; n < palette.size() && n < 64;
208 n++) { // Limit to 64 for popup
209 ImGui::PushID(n);
210 if ((n % 8) != 0) {
211 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
212 }
213
214 if (SnesColorButton(
215 "##popuppal", palette[n],
216 ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
217 ImVec2(20, 20))) {
218 selected_color = n;
219 current_color = ConvertSnesColorToImVec4(palette[n]);
220 }
221
222 ImGui::PopID();
223 }
224 ImGui::EndGroup();
225
226 ImGui::EndPopup();
227 }
228
229 return modified;
230}
231
232// ============================================================================
233// Legacy Functions (for compatibility)
234// ============================================================================
235
236IMGUI_API bool DisplayPalette(gfx::SnesPalette& palette, bool loaded) {
237 static ImVec4 color = ImVec4(0, 0, 0, 255.f);
238 ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_AlphaPreview |
239 ImGuiColorEditFlags_NoDragDrop |
240 ImGuiColorEditFlags_NoOptions;
241
242 // Generate a default palette. The palette will persist and can be edited.
243 static bool init = false;
244 static ImVec4 saved_palette[32] = {};
245 if (loaded && !init) {
246 for (int n = 0; n < palette.size(); n++) {
247 auto color = palette[n];
248 saved_palette[n].x = color.rgb().x / 255;
249 saved_palette[n].y = color.rgb().y / 255;
250 saved_palette[n].z = color.rgb().z / 255;
251 saved_palette[n].w = 255; // Alpha
252 }
253 init = true;
254 }
255
256 static ImVec4 backup_color;
257 ImGui::Text("Current ==>");
258 ImGui::SameLine();
259 ImGui::Text("Previous");
260
261 ImGui::ColorButton(
262 "##current", color,
263 ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
264 ImVec2(60, 40));
265 ImGui::SameLine();
266
267 if (ImGui::ColorButton(
268 "##previous", backup_color,
269 ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
270 ImVec2(60, 40)))
271 color = backup_color;
272 ImGui::Separator();
273
274 ImGui::BeginGroup(); // Lock X position
275 ImGui::Text("Palette");
276 for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) {
277 ImGui::PushID(n);
278 if ((n % 4) != 0)
279 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
280
281 ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha |
282 ImGuiColorEditFlags_NoPicker |
283 ImGuiColorEditFlags_NoTooltip;
284 if (ImGui::ColorButton("##palette", saved_palette[n], palette_button_flags,
285 ImVec2(20, 20)))
286 color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z,
287 color.w); // Preserve alpha!
288
289 if (ImGui::BeginDragDropTarget()) {
290 if (const ImGuiPayload* payload =
291 ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
292 memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3);
293 if (const ImGuiPayload* payload =
294 ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
295 memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4);
296 ImGui::EndDragDropTarget();
297 }
298
299 ImGui::PopID();
300 }
301 ImGui::EndGroup();
302 ImGui::SameLine();
303
304 ImGui::ColorPicker4("##picker", (float*)&color,
305 misc_flags | ImGuiColorEditFlags_NoSidePreview |
306 ImGuiColorEditFlags_NoSmallPreview);
307 return true;
308}
309
310void SelectablePalettePipeline(uint64_t& palette_id, bool& refresh_graphics,
311 gfx::SnesPalette& palette) {
312 const auto palette_row_size = 7;
313 if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)100);
314 ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
315 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
316 ImGui::BeginGroup(); // Lock X position
317 ImGui::Text("Palette");
318 for (int n = 0; n < palette.size(); n++) {
319 ImGui::PushID(n);
320 if ((n % palette_row_size) != 0)
321 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
322
323 // Check if the current row is selected
324 bool is_selected = (palette_id == n / palette_row_size);
325
326 // Add outline rectangle to the selected row
327 if (is_selected) {
328 ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 1.0f, 0.0f, 1.0f));
329 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.0f);
330 }
331
332 if (gui::SnesColorButton("##palette", palette[n],
333 ImGuiColorEditFlags_NoAlpha |
334 ImGuiColorEditFlags_NoPicker |
335 ImGuiColorEditFlags_NoTooltip,
336 ImVec2(20, 20))) {
337 palette_id = n / palette_row_size;
338 refresh_graphics = true;
339 }
340
341 if (is_selected) {
342 ImGui::PopStyleColor();
343 ImGui::PopStyleVar();
344 }
345
346 ImGui::PopID();
347 }
348 ImGui::EndGroup();
349 }
350 ImGui::EndChild();
351}
352
354 const std::string& title,
355 bool show_color_picker, int colors_per_row,
356 ImGuiColorEditFlags flags) {
357 // Default flags if none provided
358 if (flags == 0) {
359 flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker |
360 ImGuiColorEditFlags_NoTooltip;
361 }
362
363 // Display title if provided
364 if (!title.empty()) {
365 ImGui::Text("%s", title.c_str());
366 }
367 static int selected_color = 0;
368
369 if (show_color_picker) {
370 ImGui::Separator();
371 static ImVec4 current_color = ImVec4(0, 0, 0, 1.0f);
372
373 if (ImGui::ColorPicker4("Color Picker", (float*)&current_color,
374 ImGuiColorEditFlags_NoSidePreview |
375 ImGuiColorEditFlags_NoSmallPreview)) {
376 // Convert the selected color to SNES format and add it to the palette
377 gfx::SnesColor snes_color(current_color);
378 palette.UpdateColor(selected_color, snes_color);
379 }
380 }
381
382 // Display the palette colors in a grid
383 ImGui::BeginGroup(); // Lock X position
384 for (int n = 0; n < palette.size(); n++) {
385 ImGui::PushID(n);
386 if ((n % colors_per_row) != 0) {
387 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
388 }
389
390 // Create a unique ID for this color button
391 std::string button_id = "##palette_" + std::to_string(n);
392
393 // Display the color button
394 if (SnesColorButton(button_id, palette[n], flags, ImVec2(20, 20))) {
395 // Color was clicked, could be used to select this color
396 selected_color = n;
397 }
398
399 if (ImGui::BeginPopupContextItem()) {
400 if (ImGui::MenuItem("Edit Color")) {
401 // Open color picker for this color
402 ImGui::OpenPopup(("Edit Color##" + std::to_string(n)).c_str());
403 }
404
405 if (ImGui::MenuItem("Copy as SNES Value")) {
406 std::string clipboard = absl::StrFormat("$%04X", palette[n].snes());
407 ImGui::SetClipboardText(clipboard.c_str());
408 }
409
410 if (ImGui::MenuItem("Copy as RGB")) {
411 auto rgb = palette[n].rgb();
412 // rgb is already in 0-255 range, no need to multiply
413 std::string clipboard =
414 absl::StrFormat("(%d,%d,%d)", (int)rgb.x, (int)rgb.y, (int)rgb.z);
415 ImGui::SetClipboardText(clipboard.c_str());
416 }
417
418 if (ImGui::MenuItem("Copy as Hex")) {
419 auto rgb = palette[n].rgb();
420 // rgb is already in 0-255 range, no need to multiply
421 std::string clipboard = absl::StrFormat("#%02X%02X%02X", (int)rgb.x,
422 (int)rgb.y, (int)rgb.z);
423 ImGui::SetClipboardText(clipboard.c_str());
424 }
425
426 ImGui::EndPopup();
427 }
428
429 // Color picker popup
430 if (ImGui::BeginPopup(("Edit Color##" + std::to_string(n)).c_str())) {
431 ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoSidePreview |
432 ImGuiColorEditFlags_NoSmallPreview;
433
434 ImVec4 color = ConvertSnesColorToImVec4(palette[n]);
435 if (ImGui::ColorPicker4("##picker", (float*)&color, picker_flags)) {
436 // Update the SNES color when the picker changes
437 palette[n] = ConvertImVec4ToSnesColor(color);
438 }
439
440 ImGui::EndPopup();
441 }
442
443 ImGui::PopID();
444 }
445 ImGui::EndGroup();
446
447 return absl::OkStatus();
448}
449
450IMGUI_API bool PaletteColorButton(const char* id, const gfx::SnesColor& color,
451 bool is_selected, bool is_modified,
452 const ImVec2& size,
453 ImGuiColorEditFlags flags) {
454 ImVec4 display_color = ConvertSnesColorToImVec4(color);
455
456 // Add visual indicators for selection and modification
457 ImGui::PushID(id);
458
459 // Selection border
460 if (is_selected) {
461 ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 0.8f, 0.0f, 1.0f));
462 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.0f);
463 }
464
465 bool clicked = ImGui::ColorButton(id, display_color, flags, size);
466
467 if (is_selected) {
468 ImGui::PopStyleVar();
469 ImGui::PopStyleColor();
470 }
471
472 // Modification indicator (small dot in corner)
473 if (is_modified) {
474 ImVec2 pos = ImGui::GetItemRectMin();
475 ImVec2 dot_pos = ImVec2(pos.x + size.x - 6, pos.y + 2);
476 ImGui::GetWindowDrawList()->AddCircleFilled(dot_pos, 3.0f,
477 IM_COL32(255, 128, 0, 255));
478 }
479
480 // Tooltip with color info
481 if (ImGui::IsItemHovered()) {
482 ImGui::BeginTooltip();
483 ImGui::Text("SNES: $%04X", color.snes());
484 auto rgb = color.rgb();
485 ImGui::Text("RGB: (%d, %d, %d)", static_cast<int>(rgb.x),
486 static_cast<int>(rgb.y), static_cast<int>(rgb.z));
487 if (is_modified) {
488 ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), "Modified");
489 }
490 ImGui::EndTooltip();
491 }
492
493 ImGui::PopID();
494 return clicked;
495}
496
497} // namespace gui
498} // namespace yaze
SNES Color container.
Definition snes_color.h:110
constexpr ImVec4 rgb() const
Get RGB values (WARNING: stored as 0-255 in ImVec4)
Definition snes_color.h:183
constexpr uint16_t snes() const
Get SNES 15-bit color.
Definition snes_color.h:193
void set_rgb(const ImVec4 val)
Set color from ImVec4 (0.0-1.0 range)
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
void UpdateColor(size_t index, const SnesColor &color)
struct snes_color snes_color
SNES color in 15-bit RGB format (BGR555)
IMGUI_API bool InlinePaletteSelector(gfx::SnesPalette &palette, int num_colors, int *selected_index)
Small inline palette selector - just color buttons for selection.
Definition color.cc:79
IMGUI_API bool SnesColorButton(absl::string_view id, gfx::SnesColor &color, ImGuiColorEditFlags flags, const ImVec2 &size_arg)
Definition color.cc:38
IMGUI_API bool PopupPaletteEditor(const char *popup_id, gfx::SnesPalette &palette, ImGuiColorEditFlags flags)
Popup palette editor - same as inline but in a popup.
Definition color.cc:185
IMGUI_API absl::Status InlinePaletteEditor(gfx::SnesPalette &palette, const std::string &title, ImGuiColorEditFlags flags)
Full inline palette editor with color picker and copy options.
Definition color.cc:119
IMGUI_API bool PaletteColorButton(const char *id, const gfx::SnesColor &color, bool is_selected, bool is_modified, const ImVec2 &size, ImGuiColorEditFlags flags)
Definition color.cc:450
ImVec4 ConvertSnesColorToImVec4(const gfx::SnesColor &color)
Convert SnesColor to standard ImVec4 for display.
Definition color.cc:20
void SelectablePalettePipeline(uint64_t &palette_id, bool &refresh_graphics, gfx::SnesPalette &palette)
Definition color.cc:310
IMGUI_API bool SnesColorEdit4(absl::string_view label, gfx::SnesColor *color, ImGuiColorEditFlags flags)
Definition color.cc:55
IMGUI_API bool DisplayPalette(gfx::SnesPalette &palette, bool loaded)
Definition color.cc:236
gfx::SnesColor ConvertImVec4ToSnesColor(const ImVec4 &color)
Convert standard ImVec4 to SnesColor.
Definition color.cc:33
absl::Status DisplayEditablePalette(gfx::SnesPalette &palette, const std::string &title, bool show_color_picker, int colors_per_row, ImGuiColorEditFlags flags)
Definition color.cc:353
SNES color in 15-bit RGB format (BGR555)
Definition yaze.h:218