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