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