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