yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
palette_editor_widget.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <map>
5
6#include "absl/strings/str_format.h"
9#include "util/log.h"
10
11namespace yaze {
12namespace gui {
13
14// Merged implementation from PaletteWidget and PaletteEditorWidget
15
17 rom_ = rom;
19 if (rom_) {
21 }
24}
25
26// --- Embedded Draw Method (from simple editor) ---
28 if (!rom_ || !rom_->is_loaded()) {
29 ImGui::TextColored(ImVec4(1, 0, 0, 1), "ROM not loaded");
30 return;
31 }
32
33 ImGui::BeginGroup();
34
36 ImGui::Separator();
37
38 auto &dungeon_pal_group = rom_->mutable_palette_group()->dungeon_main;
39 if (current_palette_id_ >= 0 &&
40 current_palette_id_ < (int)dungeon_pal_group.size()) {
41 auto palette = dungeon_pal_group[current_palette_id_];
42 DrawPaletteGrid(palette, 15);
43 dungeon_pal_group[current_palette_id_] = palette;
44 }
45
46 ImGui::Separator();
47
48 if (selected_color_index_ >= 0) {
50 } else {
51 ImGui::TextDisabled("Select a color to edit");
52 }
53
54 ImGui::EndGroup();
55}
56
58 auto &dungeon_pal_group = rom_->mutable_palette_group()->dungeon_main;
59 int num_palettes = dungeon_pal_group.size();
60
61 ImGui::Text("Dungeon Palette:");
62 ImGui::SameLine();
63
64 if (ImGui::BeginCombo(
65 "##PaletteSelect",
66 absl::StrFormat("Palette %d", current_palette_id_).c_str())) {
67 for (int i = 0; i < num_palettes; i++) {
68 bool is_selected = (current_palette_id_ == i);
69 if (ImGui::Selectable(absl::StrFormat("Palette %d", i).c_str(),
70 is_selected)) {
73 }
74 if (is_selected) {
75 ImGui::SetItemDefaultFocus();
76 }
77 }
78 ImGui::EndCombo();
79 }
80}
81
83 ImGui::SeparatorText(
84 absl::StrFormat("Edit Color %d", selected_color_index_).c_str());
85
86 auto &dungeon_pal_group = rom_->mutable_palette_group()->dungeon_main;
87 auto palette = dungeon_pal_group[current_palette_id_];
88 auto original_color = palette[selected_color_index_];
89
90 if (ImGui::ColorEdit3("Color", &editing_color_.x,
91 ImGuiColorEditFlags_NoAlpha |
92 ImGuiColorEditFlags_PickerHueWheel)) {
93 int r = static_cast<int>(editing_color_.x * 31.0f);
94 int g = static_cast<int>(editing_color_.y * 31.0f);
95 int b = static_cast<int>(editing_color_.z * 31.0f);
96 uint16_t snes_color = (b << 10) | (g << 5) | r;
97
99 dungeon_pal_group[current_palette_id_] = palette;
100
103 }
104 }
105
106 ImGui::Text("RGB (0-255): (%d, %d, %d)", (int)(editing_color_.x * 255),
107 (int)(editing_color_.y * 255), (int)(editing_color_.z * 255));
108 ImGui::Text("SNES BGR555: 0x%04X", original_color.snes());
109
110 if (ImGui::Button("Reset to Original")) {
111 editing_color_ = ImVec4(original_color.rgb().x / 255.0f,
112 original_color.rgb().y / 255.0f,
113 original_color.rgb().z / 255.0f, 1.0f);
114 }
115}
116
117// --- Modal/Popup Methods (from feature-rich widget) ---
118
120 const std::string &title) {
121 if (ImGui::BeginPopupModal(title.c_str(), nullptr,
122 ImGuiWindowFlags_AlwaysAutoResize)) {
123 ImGui::Text("Enhanced Palette Editor");
124 ImGui::Separator();
125
126 DrawPaletteGrid(palette);
127 ImGui::Separator();
128
129 if (ImGui::CollapsingHeader("Palette Analysis")) {
130 DrawPaletteAnalysis(palette);
131 }
132
133 if (ImGui::CollapsingHeader("ROM Palette Manager") && rom_) {
135
136 if (ImGui::Button("Apply ROM Palette") && !rom_palette_groups_.empty()) {
138 static_cast<int>(rom_palette_groups_.size())) {
140 }
141 }
142 }
143
144 ImGui::Separator();
145
146 if (ImGui::Button("Save Backup")) {
147 SavePaletteBackup(palette);
148 }
149 ImGui::SameLine();
150 if (ImGui::Button("Restore Backup")) {
151 RestorePaletteBackup(palette);
152 }
153 ImGui::SameLine();
154 if (ImGui::Button("Close")) {
155 ImGui::CloseCurrentPopup();
156 }
157
158 ImGui::EndPopup();
159 }
160}
161
163 if (!show_rom_manager_) return;
164
165 if (ImGui::Begin("ROM Palette Manager", &show_rom_manager_)) {
166 if (!rom_) {
167 ImGui::Text("No ROM loaded");
168 ImGui::End();
169 return;
170 }
171
174 }
175
177
178 if (current_group_index_ < static_cast<int>(rom_palette_groups_.size())) {
179 ImGui::Separator();
180 ImGui::Text("Preview of %s:",
182
183 const auto &preview_palette = rom_palette_groups_[current_group_index_];
184 DrawPaletteGrid(const_cast<gfx::SnesPalette &>(preview_palette));
185 DrawPaletteAnalysis(preview_palette);
186 }
187 }
188 ImGui::End();
189}
190
192 const std::string &title) {
193 if (!show_color_analysis_) return;
194
195 if (ImGui::Begin(title.c_str(), &show_color_analysis_)) {
196 ImGui::Text("Bitmap Color Analysis");
197 ImGui::Separator();
198
199 if (!bitmap.is_active()) {
200 ImGui::Text("Bitmap is not active");
201 ImGui::End();
202 return;
203 }
204
205 std::map<uint8_t, int> pixel_counts;
206 const auto &data = bitmap.vector();
207
208 for (uint8_t pixel : data) {
209 uint8_t palette_index = pixel & 0x0F;
210 pixel_counts[palette_index]++;
211 }
212
213 ImGui::Text("Bitmap Size: %d x %d (%zu pixels)", bitmap.width(),
214 bitmap.height(), data.size());
215
216 ImGui::Separator();
217 ImGui::Text("Pixel Distribution:");
218
219 int total_pixels = static_cast<int>(data.size());
220 for (const auto &[index, count] : pixel_counts) {
221 float percentage = (static_cast<float>(count) / total_pixels) * 100.0f;
222 ImGui::Text("Index %d: %d pixels (%.1f%%)", index, count, percentage);
223
224 ImGui::SameLine();
225 ImGui::ProgressBar(percentage / 100.0f, ImVec2(100, 0));
226
227 if (index < static_cast<int>(bitmap.palette().size())) {
228 ImGui::SameLine();
229 auto color = bitmap.palette()[index];
230 ImVec4 display_color = color.rgb();
231 ImGui::ColorButton(("##color" + std::to_string(index)).c_str(),
232 display_color, ImGuiColorEditFlags_NoTooltip,
233 ImVec2(20, 20));
234 if (ImGui::IsItemHovered()) {
235 ImGui::SetTooltip("SNES Color: 0x%04X\nRGB: (%d, %d, %d)",
236 color.snes(),
237 static_cast<int>(display_color.x * 255),
238 static_cast<int>(display_color.y * 255),
239 static_cast<int>(display_color.z * 255));
240 }
241 }
242 }
243 }
244 ImGui::End();
245}
246
248 int palette_index) {
249 if (!bitmap || !rom_palettes_loaded_ || group_index < 0 ||
250 group_index >= static_cast<int>(rom_palette_groups_.size())) {
251 return false;
252 }
253
254 try {
255 const auto &selected_palette = rom_palette_groups_[group_index];
256 SavePaletteBackup(bitmap->palette());
257
258 if (palette_index >= 0 && palette_index < 8) {
259 bitmap->SetPaletteWithTransparent(selected_palette, palette_index);
260 } else {
261 bitmap->SetPalette(selected_palette);
262 }
263
266
267 current_group_index_ = group_index;
268 current_palette_index_ = palette_index;
269 return true;
270 } catch (const std::exception &e) {
271 return false;
272 }
273}
274
277 current_group_index_ >= static_cast<int>(rom_palette_groups_.size())) {
278 return nullptr;
279 }
281}
282
286
288 if (backup_palette_.size() == 0) {
289 return false;
290 }
291 palette = backup_palette_;
292 return true;
293}
294
295// Unified grid drawing function
297 for (int i = 0; i < static_cast<int>(palette.size()); i++) {
298 if (i % cols != 0) ImGui::SameLine();
299
300 auto color = palette[i];
301 ImVec4 display_color = color.rgb();
302
303 ImGui::PushID(i);
304 if (ImGui::ColorButton("##color", display_color,
305 ImGuiColorEditFlags_NoTooltip, ImVec2(30, 30))) {
308 temp_color_ = display_color;
309 editing_color_ = display_color;
310 }
311
312 if (ImGui::BeginPopupContextItem()) {
313 ImGui::Text("Color %d (0x%04X)", i, color.snes());
314 ImGui::Separator();
315 if (ImGui::MenuItem("Edit Color")) {
318 temp_color_ = display_color;
319 editing_color_ = display_color;
320 }
321 if (ImGui::MenuItem("Reset to Black")) {
322 palette[i] = gfx::SnesColor(0);
323 }
324 ImGui::EndPopup();
325 }
326
327 if (ImGui::IsItemHovered()) {
328 ImGui::SetTooltip("Color %d\nSNES: 0x%04X\nRGB: (%d, %d, %d)", i,
329 color.snes(), static_cast<int>(display_color.x * 255),
330 static_cast<int>(display_color.y * 255),
331 static_cast<int>(display_color.z * 255));
332 }
333
334 ImGui::PopID();
335 }
336
337 if (editing_color_index_ >= 0) {
338 ImGui::OpenPopup("Edit Color");
339 if (ImGui::BeginPopupModal("Edit Color", nullptr,
340 ImGuiWindowFlags_AlwaysAutoResize)) {
341 ImGui::Text("Editing Color %d", editing_color_index_);
342 if (ImGui::ColorEdit4("Color", &temp_color_.x,
343 ImGuiColorEditFlags_NoAlpha |
344 ImGuiColorEditFlags_DisplayRGB)) {
345 auto new_snes_color = gfx::SnesColor(temp_color_);
346 palette[editing_color_index_] = new_snes_color;
347 }
348 if (ImGui::Button("Apply")) {
350 ImGui::CloseCurrentPopup();
351 }
352 ImGui::SameLine();
353 if (ImGui::Button("Cancel")) {
355 ImGui::CloseCurrentPopup();
356 }
357 ImGui::EndPopup();
358 }
359 }
360}
361
365 }
366
367 if (rom_palette_groups_.empty()) {
368 ImGui::Text("No ROM palettes available");
369 return;
370 }
371
372 ImGui::Text("Palette Group:");
373 if (ImGui::Combo(
374 "##PaletteGroup", &current_group_index_,
375 [](void *data, int idx, const char **out_text) -> bool {
376 auto *names = static_cast<std::vector<std::string> *>(data);
377 if (idx < 0 || idx >= static_cast<int>(names->size())) return false;
378 *out_text = (*names)[idx].c_str();
379 return true;
380 },
382 static_cast<int>(palette_group_names_.size()))) {
383 }
384
385 ImGui::Text("Palette Index:");
386 ImGui::SliderInt("##PaletteIndex", &current_palette_index_, 0, 7, "%d");
387
388 if (current_group_index_ < static_cast<int>(rom_palette_groups_.size())) {
389 ImGui::Text("Preview:");
390 const auto &preview_palette = rom_palette_groups_[current_group_index_];
391 for (int i = 0; i < 8 && i < static_cast<int>(preview_palette.size());
392 i++) {
393 if (i > 0) ImGui::SameLine();
394 auto color = preview_palette[i];
395 ImVec4 display_color = color.rgb();
396 ImGui::ColorButton(("##preview" + std::to_string(i)).c_str(),
397 display_color, ImGuiColorEditFlags_NoTooltip,
398 ImVec2(20, 20));
399 }
400 }
401}
402
404 int color_index) {
405 ImVec4 rgba = color.rgb();
406
407 ImGui::PushID(color_index);
408
409 if (ImGui::ColorEdit4("##color_edit", &rgba.x,
410 ImGuiColorEditFlags_NoAlpha |
411 ImGuiColorEditFlags_DisplayRGB)) {
412 color = gfx::SnesColor(rgba);
413 }
414
415 ImGui::Text("SNES Color: 0x%04X", color.snes());
416
417 int r = (color.snes() & 0x1F);
418 int g = (color.snes() >> 5) & 0x1F;
419 int b = (color.snes() >> 10) & 0x1F;
420
421 if (ImGui::SliderInt("Red", &r, 0, 31)) {
422 uint16_t new_color = (color.snes() & 0xFFE0) | (r & 0x1F);
423 color = gfx::SnesColor(new_color);
424 }
425 if (ImGui::SliderInt("Green", &g, 0, 31)) {
426 uint16_t new_color = (color.snes() & 0xFC1F) | ((g & 0x1F) << 5);
427 color = gfx::SnesColor(new_color);
428 }
429 if (ImGui::SliderInt("Blue", &b, 0, 31)) {
430 uint16_t new_color = (color.snes() & 0x83FF) | ((b & 0x1F) << 10);
431 color = gfx::SnesColor(new_color);
432 }
433
434 ImGui::PopID();
435}
436
438 const gfx::SnesPalette &palette) {
439 ImGui::Text("Palette Information:");
440 ImGui::Text("Size: %zu colors", palette.size());
441
442 std::map<uint16_t, int> color_frequency;
443 for (int i = 0; i < static_cast<int>(palette.size()); i++) {
444 color_frequency[palette[i].snes()]++;
445 }
446
447 ImGui::Text("Unique Colors: %zu", color_frequency.size());
448
449 if (color_frequency.size() < palette.size()) {
450 ImGui::TextColored(ImVec4(1, 1, 0, 1), "Warning: Duplicate colors detected!");
451 if (ImGui::TreeNode("Duplicate Colors")) {
452 for (const auto &[snes_color, count] : color_frequency) {
453 if (count > 1) {
454 ImVec4 display_color = gfx::SnesColor(snes_color).rgb();
455 ImGui::ColorButton(("##dup" + std::to_string(snes_color)).c_str(),
456 display_color, ImGuiColorEditFlags_NoTooltip,
457 ImVec2(16, 16));
458 ImGui::SameLine();
459 ImGui::Text("0x%04X appears %d times", snes_color, count);
460 }
461 }
462 ImGui::TreePop();
463 }
464 }
465
466 float total_brightness = 0.0f;
467 float min_brightness = 1.0f;
468 float max_brightness = 0.0f;
469
470 for (int i = 0; i < static_cast<int>(palette.size()); i++) {
471 ImVec4 color = palette[i].rgb();
472 float brightness = (color.x + color.y + color.z) / 3.0f;
473 total_brightness += brightness;
474 min_brightness = std::min(min_brightness, brightness);
475 max_brightness = std::max(max_brightness, brightness);
476 }
477
478 float avg_brightness = total_brightness / palette.size();
479
480 ImGui::Separator();
481 ImGui::Text("Brightness Analysis:");
482 ImGui::Text("Average: %.2f", avg_brightness);
483 ImGui::Text("Range: %.2f - %.2f", min_brightness, max_brightness);
484
485 ImGui::Text("Brightness Distribution:");
486 ImGui::ProgressBar(avg_brightness, ImVec2(-1, 0), "Avg");
487}
488
490 if (!rom_ || rom_palettes_loaded_) return;
491
492 try {
493 const auto &palette_groups = rom_->palette_group();
494 rom_palette_groups_.clear();
495 palette_group_names_.clear();
496
497 if (palette_groups.overworld_main.size() > 0) {
498 rom_palette_groups_.push_back(palette_groups.overworld_main[0]);
499 palette_group_names_.push_back("Overworld Main");
500 }
501 if (palette_groups.overworld_aux.size() > 0) {
502 rom_palette_groups_.push_back(palette_groups.overworld_aux[0]);
503 palette_group_names_.push_back("Overworld Aux");
504 }
505 if (palette_groups.overworld_animated.size() > 0) {
506 rom_palette_groups_.push_back(palette_groups.overworld_animated[0]);
507 palette_group_names_.push_back("Overworld Animated");
508 }
509 if (palette_groups.dungeon_main.size() > 0) {
510 rom_palette_groups_.push_back(palette_groups.dungeon_main[0]);
511 palette_group_names_.push_back("Dungeon Main");
512 }
513 if (palette_groups.global_sprites.size() > 0) {
514 rom_palette_groups_.push_back(palette_groups.global_sprites[0]);
515 palette_group_names_.push_back("Global Sprites");
516 }
517 if (palette_groups.armors.size() > 0) {
518 rom_palette_groups_.push_back(palette_groups.armors[0]);
519 palette_group_names_.push_back("Armor");
520 }
521 if (palette_groups.swords.size() > 0) {
522 rom_palette_groups_.push_back(palette_groups.swords[0]);
523 palette_group_names_.push_back("Swords");
524 }
525
527 } catch (const std::exception &e) {
528 LOG_ERROR("Enhanced Palette Editor", "Failed to load ROM palettes");
529 }
530}
531
532} // namespace gui
533} // namespace yaze
534
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:74
auto palette_group() const
Definition rom.h:216
auto mutable_palette_group()
Definition rom.h:217
bool is_loaded() const
Definition rom.h:200
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:32
static Arena & Get()
Definition arena.cc:15
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:66
const SnesPalette & palette() const
Definition bitmap.h:277
const std::vector< uint8_t > & vector() const
Definition bitmap.h:290
bool is_active() const
Definition bitmap.h:293
int height() const
Definition bitmap.h:283
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap.
Definition bitmap.cc:334
int width() const
Definition bitmap.h:282
void SetPaletteWithTransparent(const SnesPalette &palette, size_t index, int length=7)
Set the palette with a transparent color.
Definition bitmap.cc:403
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
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
std::vector< gfx::SnesPalette > rom_palette_groups_
std::function< void(int palette_id)> on_palette_changed_
bool RestorePaletteBackup(gfx::SnesPalette &palette)
void ShowPaletteEditor(gfx::SnesPalette &palette, const std::string &title="Palette Editor")
bool ApplyROMPalette(gfx::Bitmap *bitmap, int group_index, int palette_index)
const gfx::SnesPalette * GetSelectedROMPalette() const
void SavePaletteBackup(const gfx::SnesPalette &palette)
void ShowColorAnalysis(const gfx::Bitmap &bitmap, const std::string &title="Color Analysis")
void DrawColorEditControls(gfx::SnesColor &color, int color_index)
void DrawPaletteAnalysis(const gfx::SnesPalette &palette)
std::vector< std::string > palette_group_names_
void DrawPaletteGrid(gfx::SnesPalette &palette, int cols=15)
#define LOG_ERROR(category, format,...)
Definition log.h:110
Main namespace for the application.
Definition controller.cc:20
SNES color in 15-bit RGB format (BGR555)
Definition yaze.h:208