#include "app/gui/bpp_format_ui.h" #include #include #include "app/gfx/bpp_format_manager.h" #include "app/gfx/bitmap.h" #include "app/gui/ui_helpers.h" #include "imgui/imgui.h" namespace yaze { namespace gui { BppFormatUI::BppFormatUI(const std::string& id) : id_(id), selected_format_(gfx::BppFormat::kBpp8), preview_format_(gfx::BppFormat::kBpp8), show_analysis_(false), show_preview_(false), show_sheet_analysis_(false), format_changed_(false), last_analysis_sheet_("") { } bool BppFormatUI::RenderFormatSelector(gfx::Bitmap* bitmap, const gfx::SnesPalette& palette, std::function on_format_changed) { if (!bitmap) return false; format_changed_ = false; ImGui::BeginGroup(); ImGui::Text("BPP Format Selection"); ImGui::Separator(); // Current format detection gfx::BppFormat current_format = gfx::BppFormatManager::Get().DetectFormat( bitmap->vector(), bitmap->width(), bitmap->height()); ImGui::Text("Current Format: %s", gfx::BppFormatManager::Get().GetFormatInfo(current_format).name.c_str()); // Format selection ImGui::Text("Target Format:"); ImGui::SameLine(); const char* format_names[] = {"2BPP", "3BPP", "4BPP", "8BPP"}; int current_selection = static_cast(selected_format_) - 2; // Convert to 0-based index if (ImGui::Combo("##BppFormat", ¤t_selection, format_names, 4)) { selected_format_ = static_cast(current_selection + 2); format_changed_ = true; } // Format information const auto& format_info = gfx::BppFormatManager::Get().GetFormatInfo(selected_format_); ImGui::Text("Max Colors: %d", format_info.max_colors); ImGui::Text("Bytes per Tile: %d", format_info.bytes_per_tile); ImGui::Text("Description: %s", format_info.description.c_str()); // Conversion efficiency if (current_format != selected_format_) { int efficiency = GetConversionEfficiency(current_format, selected_format_); ImGui::Text("Conversion Efficiency: %d%%", efficiency); ImVec4 efficiency_color; if (efficiency >= 80) { efficiency_color = GetSuccessColor(); // Green } else if (efficiency >= 60) { efficiency_color = GetWarningColor(); // Yellow } else { efficiency_color = GetErrorColor(); // Red } ImGui::TextColored(efficiency_color, "Quality: %s", efficiency >= 80 ? "Excellent" : efficiency >= 60 ? "Good" : "Poor"); } // Action buttons ImGui::Separator(); if (ImGui::Button("Convert Format")) { if (on_format_changed) { on_format_changed(selected_format_); } format_changed_ = true; } ImGui::SameLine(); if (ImGui::Button("Show Analysis")) { show_analysis_ = !show_analysis_; } ImGui::SameLine(); if (ImGui::Button("Preview Conversion")) { show_preview_ = !show_preview_; preview_format_ = selected_format_; } ImGui::EndGroup(); // Analysis panel if (show_analysis_) { RenderAnalysisPanel(*bitmap, palette); } // Preview panel if (show_preview_) { RenderConversionPreview(*bitmap, preview_format_, palette); } return format_changed_; } void BppFormatUI::RenderAnalysisPanel(const gfx::Bitmap& bitmap, const gfx::SnesPalette& palette) { ImGui::Begin("BPP Format Analysis", &show_analysis_); // Basic analysis gfx::BppFormat detected_format = gfx::BppFormatManager::Get().DetectFormat( bitmap.vector(), bitmap.width(), bitmap.height()); ImGui::Text("Detected Format: %s", gfx::BppFormatManager::Get().GetFormatInfo(detected_format).name.c_str()); // Color usage analysis std::vector color_usage(256, 0); for (uint8_t pixel : bitmap.vector()) { color_usage[pixel]++; } int used_colors = 0; for (int count : color_usage) { if (count > 0) used_colors++; } ImGui::Text("Colors Used: %d / %d", used_colors, static_cast(palette.size())); ImGui::Text("Color Efficiency: %.1f%%", (static_cast(used_colors) / palette.size()) * 100.0f); // Color usage chart if (ImGui::CollapsingHeader("Color Usage Chart")) { RenderColorUsageChart(color_usage); } // Format recommendations ImGui::Separator(); ImGui::Text("Format Recommendations:"); if (used_colors <= 4) { ImGui::TextColored(GetSuccessColor(), "✓ 2BPP format would be optimal"); } else if (used_colors <= 8) { ImGui::TextColored(GetSuccessColor(), "✓ 3BPP format would be optimal"); } else if (used_colors <= 16) { ImGui::TextColored(GetSuccessColor(), "✓ 4BPP format would be optimal"); } else { ImGui::TextColored(GetWarningColor(), "⚠ 8BPP format is necessary"); } // Memory usage comparison if (ImGui::CollapsingHeader("Memory Usage Comparison")) { const auto& current_info = gfx::BppFormatManager::Get().GetFormatInfo(detected_format); int current_bytes = (bitmap.width() * bitmap.height() * current_info.bits_per_pixel) / 8; ImGui::Text("Current Format (%s): %d bytes", current_info.name.c_str(), current_bytes); for (auto format : gfx::BppFormatManager::Get().GetAvailableFormats()) { if (format == detected_format) continue; const auto& info = gfx::BppFormatManager::Get().GetFormatInfo(format); int format_bytes = (bitmap.width() * bitmap.height() * info.bits_per_pixel) / 8; float ratio = static_cast(format_bytes) / current_bytes; ImGui::Text("%s: %d bytes (%.1fx)", info.name.c_str(), format_bytes, ratio); } } ImGui::End(); } void BppFormatUI::RenderConversionPreview(const gfx::Bitmap& bitmap, gfx::BppFormat target_format, const gfx::SnesPalette& palette) { ImGui::Begin("BPP Conversion Preview", &show_preview_); gfx::BppFormat current_format = gfx::BppFormatManager::Get().DetectFormat( bitmap.vector(), bitmap.width(), bitmap.height()); if (current_format == target_format) { ImGui::Text("No conversion needed - formats are identical"); ImGui::End(); return; } // Convert the bitmap auto converted_data = gfx::BppFormatManager::Get().ConvertFormat( bitmap.vector(), current_format, target_format, bitmap.width(), bitmap.height()); // Create preview bitmap gfx::Bitmap preview_bitmap(bitmap.width(), bitmap.height(), bitmap.depth(), converted_data, palette); // Render side-by-side comparison ImGui::Text("Original (%s) vs Converted (%s)", gfx::BppFormatManager::Get().GetFormatInfo(current_format).name.c_str(), gfx::BppFormatManager::Get().GetFormatInfo(target_format).name.c_str()); ImGui::Columns(2, "PreviewColumns"); // Original ImGui::Text("Original"); if (bitmap.texture()) { ImGui::Image((ImTextureID)(intptr_t)bitmap.texture(), ImVec2(256, 256 * bitmap.height() / bitmap.width())); } ImGui::NextColumn(); // Converted ImGui::Text("Converted"); if (preview_bitmap.texture()) { ImGui::Image((ImTextureID)(intptr_t)preview_bitmap.texture(), ImVec2(256, 256 * preview_bitmap.height() / preview_bitmap.width())); } ImGui::Columns(1); // Conversion statistics ImGui::Separator(); ImGui::Text("Conversion Statistics:"); const auto& from_info = gfx::BppFormatManager::Get().GetFormatInfo(current_format); const auto& to_info = gfx::BppFormatManager::Get().GetFormatInfo(target_format); int from_bytes = (bitmap.width() * bitmap.height() * from_info.bits_per_pixel) / 8; int to_bytes = (bitmap.width() * bitmap.height() * to_info.bits_per_pixel) / 8; ImGui::Text("Size: %d bytes -> %d bytes", from_bytes, to_bytes); ImGui::Text("Compression Ratio: %.2fx", static_cast(from_bytes) / to_bytes); ImGui::End(); } void BppFormatUI::RenderSheetAnalysis(const std::vector& sheet_data, int sheet_id, const gfx::SnesPalette& palette) { std::string analysis_key = "sheet_" + std::to_string(sheet_id); // Check if we need to update analysis if (last_analysis_sheet_ != analysis_key) { auto analysis = gfx::BppFormatManager::Get().AnalyzeGraphicsSheet(sheet_data, sheet_id, palette); UpdateAnalysisCache(sheet_id, analysis); last_analysis_sheet_ = analysis_key; } auto it = cached_analysis_.find(sheet_id); if (it == cached_analysis_.end()) return; const auto& analysis = it->second; ImGui::Begin("Graphics Sheet Analysis", &show_sheet_analysis_); ImGui::Text("Sheet ID: %d", analysis.sheet_id); ImGui::Text("Original Format: %s", gfx::BppFormatManager::Get().GetFormatInfo(analysis.original_format).name.c_str()); ImGui::Text("Current Format: %s", gfx::BppFormatManager::Get().GetFormatInfo(analysis.current_format).name.c_str()); if (analysis.was_converted) { ImGui::TextColored(GetWarningColor(), "⚠ This sheet was converted"); ImGui::Text("Conversion History: %s", analysis.conversion_history.c_str()); } else { ImGui::TextColored(GetSuccessColor(), "✓ Original format preserved"); } ImGui::Separator(); ImGui::Text("Color Usage: %d / %d colors used", analysis.palette_entries_used, static_cast(palette.size())); ImGui::Text("Compression Ratio: %.2fx", analysis.compression_ratio); ImGui::Text("Size: %zu -> %zu bytes", analysis.original_size, analysis.current_size); // Tile usage pattern if (ImGui::CollapsingHeader("Tile Usage Pattern")) { int total_tiles = analysis.tile_usage_pattern.size(); int used_tiles = 0; int empty_tiles = 0; for (int usage : analysis.tile_usage_pattern) { if (usage > 0) { used_tiles++; } else { empty_tiles++; } } ImGui::Text("Total Tiles: %d", total_tiles); ImGui::Text("Used Tiles: %d (%.1f%%)", used_tiles, (static_cast(used_tiles) / total_tiles) * 100.0f); ImGui::Text("Empty Tiles: %d (%.1f%%)", empty_tiles, (static_cast(empty_tiles) / total_tiles) * 100.0f); } // Recommendations ImGui::Separator(); ImGui::Text("Recommendations:"); if (analysis.was_converted && analysis.palette_entries_used <= 16) { ImGui::TextColored(GetSuccessColor(), "✓ Consider reverting to %s format for better compression", gfx::BppFormatManager::Get().GetFormatInfo(analysis.original_format).name.c_str()); } if (analysis.palette_entries_used < static_cast(palette.size()) / 2) { ImGui::TextColored(GetWarningColor(), "⚠ Palette is underutilized - consider optimization"); } ImGui::End(); } bool BppFormatUI::IsConversionAvailable(gfx::BppFormat from_format, gfx::BppFormat to_format) const { // All conversions are available in our implementation return from_format != to_format; } int BppFormatUI::GetConversionEfficiency(gfx::BppFormat from_format, gfx::BppFormat to_format) const { // Calculate efficiency based on format compatibility if (from_format == to_format) return 100; // Higher BPP to lower BPP conversions may lose quality if (static_cast(from_format) > static_cast(to_format)) { int bpp_diff = static_cast(from_format) - static_cast(to_format); return std::max(20, 100 - (bpp_diff * 20)); // Reduce efficiency by 20% per BPP level } // Lower BPP to higher BPP conversions are lossless return 100; } void BppFormatUI::RenderFormatInfo(const gfx::BppFormatInfo& info) { ImGui::Text("Format: %s", info.name.c_str()); ImGui::Text("Bits per Pixel: %d", info.bits_per_pixel); ImGui::Text("Max Colors: %d", info.max_colors); ImGui::Text("Bytes per Tile: %d", info.bytes_per_tile); ImGui::Text("Compressed: %s", info.is_compressed ? "Yes" : "No"); ImGui::Text("Description: %s", info.description.c_str()); } void BppFormatUI::RenderColorUsageChart(const std::vector& color_usage) { // Find maximum usage for scaling int max_usage = *std::max_element(color_usage.begin(), color_usage.end()); if (max_usage == 0) return; // Render simple bar chart ImGui::Text("Color Usage Distribution:"); for (size_t i = 0; i < std::min(color_usage.size(), size_t(16)); ++i) { if (color_usage[i] > 0) { float usage_ratio = static_cast(color_usage[i]) / max_usage; ImGui::Text("Color %zu: %d pixels (%.1f%%)", i, color_usage[i], (static_cast(color_usage[i]) / (16 * 16)) * 100.0f); ImGui::SameLine(); ImGui::ProgressBar(usage_ratio, ImVec2(100, 0)); } } } void BppFormatUI::RenderConversionHistory(const std::string& history) { ImGui::Text("Conversion History:"); ImGui::TextWrapped("%s", history.c_str()); } std::string BppFormatUI::GetFormatDescription(gfx::BppFormat format) const { return gfx::BppFormatManager::Get().GetFormatInfo(format).description; } ImVec4 BppFormatUI::GetFormatColor(gfx::BppFormat format) const { switch (format) { case gfx::BppFormat::kBpp2: return ImVec4(1, 0, 0, 1); // Red case gfx::BppFormat::kBpp3: return ImVec4(1, 1, 0, 1); // Yellow case gfx::BppFormat::kBpp4: return ImVec4(0, 1, 0, 1); // Green case gfx::BppFormat::kBpp8: return ImVec4(0, 0, 1, 1); // Blue default: return ImVec4(1, 1, 1, 1); // White } } void BppFormatUI::UpdateAnalysisCache(int sheet_id, const gfx::GraphicsSheetAnalysis& analysis) { cached_analysis_[sheet_id] = analysis; } // BppConversionDialog implementation BppConversionDialog::BppConversionDialog(const std::string& id) : id_(id), is_open_(false), target_format_(gfx::BppFormat::kBpp8), preserve_palette_(true), preview_valid_(false), show_preview_(true), preview_scale_(1.0f) { } void BppConversionDialog::Show(const gfx::Bitmap& bitmap, const gfx::SnesPalette& palette, std::function on_convert) { source_bitmap_ = bitmap; source_palette_ = palette; convert_callback_ = on_convert; is_open_ = true; preview_valid_ = false; } bool BppConversionDialog::Render() { if (!is_open_) return false; ImGui::OpenPopup("BPP Format Conversion"); if (ImGui::BeginPopupModal("BPP Format Conversion", &is_open_, ImGuiWindowFlags_AlwaysAutoResize)) { RenderFormatSelector(); ImGui::Separator(); RenderOptions(); ImGui::Separator(); if (show_preview_) { RenderPreview(); ImGui::Separator(); } RenderButtons(); ImGui::EndPopup(); } return is_open_; } void BppConversionDialog::UpdatePreview() { if (preview_valid_) return; gfx::BppFormat current_format = gfx::BppFormatManager::Get().DetectFormat( source_bitmap_.vector(), source_bitmap_.width(), source_bitmap_.height()); if (current_format == target_format_) { preview_bitmap_ = source_bitmap_; preview_valid_ = true; return; } auto converted_data = gfx::BppFormatManager::Get().ConvertFormat( source_bitmap_.vector(), current_format, target_format_, source_bitmap_.width(), source_bitmap_.height()); preview_bitmap_ = gfx::Bitmap(source_bitmap_.width(), source_bitmap_.height(), source_bitmap_.depth(), converted_data, source_palette_); preview_valid_ = true; } void BppConversionDialog::RenderFormatSelector() { ImGui::Text("Convert to BPP Format:"); const char* format_names[] = {"2BPP", "3BPP", "4BPP", "8BPP"}; int current_selection = static_cast(target_format_) - 2; if (ImGui::Combo("##TargetFormat", ¤t_selection, format_names, 4)) { target_format_ = static_cast(current_selection + 2); preview_valid_ = false; // Invalidate preview } const auto& format_info = gfx::BppFormatManager::Get().GetFormatInfo(target_format_); ImGui::Text("Max Colors: %d", format_info.max_colors); ImGui::Text("Description: %s", format_info.description.c_str()); } void BppConversionDialog::RenderPreview() { if (ImGui::Button("Update Preview")) { preview_valid_ = false; } UpdatePreview(); if (preview_valid_ && preview_bitmap_.texture()) { ImGui::Text("Preview:"); ImGui::Image((ImTextureID)(intptr_t)preview_bitmap_.texture(), ImVec2(128 * preview_scale_, 128 * preview_scale_)); ImGui::SliderFloat("Scale", &preview_scale_, 0.5f, 3.0f); } } void BppConversionDialog::RenderOptions() { ImGui::Checkbox("Preserve Palette", &preserve_palette_); ImGui::SameLine(); ImGui::Checkbox("Show Preview", &show_preview_); } void BppConversionDialog::RenderButtons() { if (ImGui::Button("Convert")) { if (convert_callback_) { convert_callback_(target_format_, preserve_palette_); } is_open_ = false; } ImGui::SameLine(); if (ImGui::Button("Cancel")) { is_open_ = false; } } // BppComparisonTool implementation BppComparisonTool::BppComparisonTool(const std::string& id) : id_(id), is_open_(false), has_source_(false), comparison_scale_(1.0f), show_metrics_(true), selected_comparison_(gfx::BppFormat::kBpp8) { } void BppComparisonTool::SetSource(const gfx::Bitmap& bitmap, const gfx::SnesPalette& palette) { source_bitmap_ = bitmap; source_palette_ = palette; has_source_ = true; GenerateComparisons(); } void BppComparisonTool::Render() { if (!is_open_ || !has_source_) return; ImGui::Begin("BPP Format Comparison", &is_open_); RenderFormatSelector(); ImGui::Separator(); RenderComparisonGrid(); if (show_metrics_) { ImGui::Separator(); RenderMetrics(); } ImGui::End(); } void BppComparisonTool::GenerateComparisons() { gfx::BppFormat source_format = gfx::BppFormatManager::Get().DetectFormat( source_bitmap_.vector(), source_bitmap_.width(), source_bitmap_.height()); for (auto format : gfx::BppFormatManager::Get().GetAvailableFormats()) { if (format == source_format) { comparison_bitmaps_[format] = source_bitmap_; comparison_palettes_[format] = source_palette_; comparison_valid_[format] = true; continue; } try { auto converted_data = gfx::BppFormatManager::Get().ConvertFormat( source_bitmap_.vector(), source_format, format, source_bitmap_.width(), source_bitmap_.height()); comparison_bitmaps_[format] = gfx::Bitmap(source_bitmap_.width(), source_bitmap_.height(), source_bitmap_.depth(), converted_data, source_palette_); comparison_palettes_[format] = source_palette_; comparison_valid_[format] = true; } catch (...) { comparison_valid_[format] = false; } } } void BppComparisonTool::RenderComparisonGrid() { ImGui::Text("Format Comparison (Scale: %.1fx)", comparison_scale_); ImGui::SliderFloat("##Scale", &comparison_scale_, 0.5f, 3.0f); ImGui::Columns(2, "ComparisonColumns"); for (auto format : gfx::BppFormatManager::Get().GetAvailableFormats()) { auto it = comparison_bitmaps_.find(format); if (it == comparison_bitmaps_.end() || !comparison_valid_[format]) continue; const auto& bitmap = it->second; const auto& format_info = gfx::BppFormatManager::Get().GetFormatInfo(format); ImGui::Text("%s", format_info.name.c_str()); if (bitmap.texture()) { ImGui::Image((ImTextureID)(intptr_t)bitmap.texture(), ImVec2(128 * comparison_scale_, 128 * comparison_scale_)); } ImGui::NextColumn(); } ImGui::Columns(1); } void BppComparisonTool::RenderMetrics() { ImGui::Text("Format Metrics:"); for (auto format : gfx::BppFormatManager::Get().GetAvailableFormats()) { if (!comparison_valid_[format]) continue; const auto& format_info = gfx::BppFormatManager::Get().GetFormatInfo(format); std::string metrics = CalculateMetrics(format); ImGui::Text("%s: %s", format_info.name.c_str(), metrics.c_str()); } } void BppComparisonTool::RenderFormatSelector() { ImGui::Text("Selected for Analysis: "); ImGui::SameLine(); const char* format_names[] = {"2BPP", "3BPP", "4BPP", "8BPP"}; int selection = static_cast(selected_comparison_) - 2; if (ImGui::Combo("##SelectedFormat", &selection, format_names, 4)) { selected_comparison_ = static_cast(selection + 2); } ImGui::SameLine(); ImGui::Checkbox("Show Metrics", &show_metrics_); } std::string BppComparisonTool::CalculateMetrics(gfx::BppFormat format) const { const auto& format_info = gfx::BppFormatManager::Get().GetFormatInfo(format); int bytes = (source_bitmap_.width() * source_bitmap_.height() * format_info.bits_per_pixel) / 8; std::ostringstream metrics; metrics << bytes << " bytes, " << format_info.max_colors << " colors"; return metrics.str(); } } // namespace gui } // namespace yaze