feat(gfx): enhance Bitmap class with palette management and metadata support

- Added methods for applying palettes based on metadata, allowing for flexible palette handling in different bitmap types.
- Introduced a new BitmapMetadata struct to track source format and palette requirements.
- Enhanced ApplyStoredPalette and SetPaletteWithTransparent methods for improved palette application and transparency handling.
- Updated SDL surface pixel management with a new UpdateSurfacePixels method for better pixel data handling.

Benefits:
- Improves the rendering capabilities of the Bitmap class by supporting various palette formats.
- Enhances user experience with more intuitive palette management in graphics operations.
This commit is contained in:
scawful
2025-10-13 17:06:10 -04:00
parent 1314d9daf9
commit 668fdc8068
10 changed files with 600 additions and 66 deletions

View File

@@ -7,12 +7,30 @@
namespace yaze {
namespace gui {
/**
* @brief Convert SnesColor to standard ImVec4 for display
*
* IMPORTANT: SnesColor.rgb() returns 0-255 values in ImVec4 (unconventional!)
* This function converts them to standard ImGui format (0.0-1.0)
*
* @param color SnesColor with internal 0-255 storage
* @return ImVec4 with standard 0.0-1.0 RGBA values for ImGui
*/
ImVec4 ConvertSnesColorToImVec4(const gfx::SnesColor& color) {
return ImVec4(color.rgb().x / 255.0f, color.rgb().y / 255.0f,
color.rgb().z / 255.0f, 1.0f);
// SnesColor stores RGB as 0-255 in ImVec4, convert to standard 0-1 range
ImVec4 rgb_255 = color.rgb();
return ImVec4(rgb_255.x / 255.0f, rgb_255.y / 255.0f,
rgb_255.z / 255.0f, 1.0f);
}
/**
* @brief Convert standard ImVec4 to SnesColor
*
* @param color ImVec4 with standard 0.0-1.0 RGBA values
* @return SnesColor with converted values
*/
gfx::SnesColor ConvertImVec4ToSnesColor(const ImVec4& color) {
// SnesColor constructor expects 0-1 range and handles conversion internally
return gfx::SnesColor(color);
}
@@ -52,6 +70,165 @@ IMGUI_API bool SnesColorEdit4(absl::string_view label, gfx::SnesColor* color,
return changed;
}
// ============================================================================
// New Standardized Palette Widgets
// ============================================================================
IMGUI_API bool InlinePaletteSelector(gfx::SnesPalette &palette,
int num_colors,
int* selected_index) {
bool selection_made = false;
int colors_to_show = std::min(num_colors, static_cast<int>(palette.size()));
ImGui::BeginGroup();
for (int n = 0; n < colors_to_show; n++) {
ImGui::PushID(n);
if (n > 0 && (n % 8) != 0) {
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
}
bool is_selected = selected_index && (*selected_index == n);
if (is_selected) {
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 1.0f, 0.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.0f);
}
if (SnesColorButton("##palettesel", palette[n],
ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
ImVec2(20, 20))) {
if (selected_index) {
*selected_index = n;
selection_made = true;
}
}
if (is_selected) {
ImGui::PopStyleVar();
ImGui::PopStyleColor();
}
ImGui::PopID();
}
ImGui::EndGroup();
return selection_made;
}
IMGUI_API absl::Status InlinePaletteEditor(gfx::SnesPalette &palette,
const std::string &title,
ImGuiColorEditFlags flags) {
if (!title.empty()) {
ImGui::Text("%s", title.c_str());
}
static int selected_color = 0;
static ImVec4 current_color = ImVec4(0, 0, 0, 1.0f);
// Color picker
ImGui::Separator();
if (ImGui::ColorPicker4("##colorpicker", (float*)&current_color,
ImGuiColorEditFlags_NoSidePreview |
ImGuiColorEditFlags_NoSmallPreview)) {
gfx::SnesColor snes_color(current_color);
palette.UpdateColor(selected_color, snes_color);
}
ImGui::Separator();
// Palette grid
ImGui::BeginGroup();
for (int n = 0; n < palette.size(); n++) {
ImGui::PushID(n);
if ((n % 8) != 0) {
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
}
if (flags == 0) {
flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker;
}
if (SnesColorButton("##palettedit", palette[n], flags, ImVec2(20, 20))) {
selected_color = n;
current_color = ConvertSnesColorToImVec4(palette[n]);
}
// Context menu
if (ImGui::BeginPopupContextItem()) {
if (ImGui::MenuItem("Copy as SNES")) {
std::string clipboard = absl::StrFormat("$%04X", palette[n].snes());
ImGui::SetClipboardText(clipboard.c_str());
}
if (ImGui::MenuItem("Copy as RGB")) {
auto rgb = palette[n].rgb();
std::string clipboard = absl::StrFormat("(%d,%d,%d)",
(int)rgb.x, (int)rgb.y, (int)rgb.z);
ImGui::SetClipboardText(clipboard.c_str());
}
if (ImGui::MenuItem("Copy as Hex")) {
auto rgb = palette[n].rgb();
std::string clipboard = absl::StrFormat("#%02X%02X%02X",
(int)rgb.x, (int)rgb.y, (int)rgb.z);
ImGui::SetClipboardText(clipboard.c_str());
}
ImGui::EndPopup();
}
ImGui::PopID();
}
ImGui::EndGroup();
return absl::OkStatus();
}
IMGUI_API bool PopupPaletteEditor(const char* popup_id,
gfx::SnesPalette &palette,
ImGuiColorEditFlags flags) {
bool modified = false;
if (ImGui::BeginPopup(popup_id)) {
static int selected_color = 0;
static ImVec4 current_color = ImVec4(0, 0, 0, 1.0f);
// Compact color picker
if (ImGui::ColorPicker4("##popuppicker", (float*)&current_color,
ImGuiColorEditFlags_NoSidePreview |
ImGuiColorEditFlags_NoSmallPreview)) {
gfx::SnesColor snes_color(current_color);
palette.UpdateColor(selected_color, snes_color);
modified = true;
}
ImGui::Separator();
// Palette grid
ImGui::BeginGroup();
for (int n = 0; n < palette.size() && n < 64; n++) { // Limit to 64 for popup
ImGui::PushID(n);
if ((n % 8) != 0) {
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
}
if (SnesColorButton("##popuppal", palette[n],
ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
ImVec2(20, 20))) {
selected_color = n;
current_color = ConvertSnesColorToImVec4(palette[n]);
}
ImGui::PopID();
}
ImGui::EndGroup();
ImGui::EndPopup();
}
return modified;
}
// ============================================================================
// Legacy Functions (for compatibility)
// ============================================================================
IMGUI_API bool DisplayPalette(gfx::SnesPalette& palette, bool loaded) {
static ImVec4 color = ImVec4(0, 0, 0, 255.f);
ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_AlphaPreview |

View File

@@ -45,6 +45,44 @@ IMGUI_API bool SnesColorButton(absl::string_view id, gfx::SnesColor &color,
IMGUI_API bool SnesColorEdit4(absl::string_view label, gfx::SnesColor *color,
ImGuiColorEditFlags flags = 0);
// ============================================================================
// Palette Widget Functions
// ============================================================================
/**
* @brief Small inline palette selector - just color buttons for selection
* @param palette Palette to display
* @param num_colors Number of colors to show (default 8)
* @param selected_index Pointer to store selected color index (optional)
* @return True if a color was selected
*/
IMGUI_API bool InlinePaletteSelector(gfx::SnesPalette &palette,
int num_colors = 8,
int* selected_index = nullptr);
/**
* @brief Full inline palette editor with color picker and copy options
* @param palette Palette to edit
* @param title Display title
* @param flags ImGui color edit flags
* @return Status of the operation
*/
IMGUI_API absl::Status InlinePaletteEditor(gfx::SnesPalette &palette,
const std::string &title = "",
ImGuiColorEditFlags flags = 0);
/**
* @brief Popup palette editor - same as inline but in a popup
* @param popup_id ID for the popup window
* @param palette Palette to edit
* @param flags ImGui color edit flags
* @return True if palette was modified
*/
IMGUI_API bool PopupPaletteEditor(const char* popup_id,
gfx::SnesPalette &palette,
ImGuiColorEditFlags flags = 0);
// Legacy functions (kept for compatibility, will be deprecated)
IMGUI_API bool DisplayPalette(gfx::SnesPalette &palette, bool loaded);
IMGUI_API absl::Status DisplayEditablePalette(gfx::SnesPalette &palette,