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