yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
canvas.cc
Go to the documentation of this file.
1#include "canvas.h"
2
3#include <cmath>
4#include <string>
6#include "app/gfx/bitmap.h"
8#include "app/gui/style.h"
11#include "imgui/imgui.h"
12
13namespace yaze::gui {
14
15
16// Define constructors and destructor in .cc to avoid incomplete type issues with unique_ptr
17
18// Default constructor
19Canvas::Canvas() : renderer_(nullptr) { InitializeDefaults(); }
20
21// Legacy constructors (renderer is optional for backward compatibility)
22Canvas::Canvas(const std::string& id)
23 : renderer_(nullptr), canvas_id_(id), context_id_(id + "Context") {
25}
26
27Canvas::Canvas(const std::string& id, ImVec2 canvas_size)
28 : renderer_(nullptr), canvas_id_(id), context_id_(id + "Context") {
32}
33
34Canvas::Canvas(const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size)
35 : renderer_(nullptr), canvas_id_(id), context_id_(id + "Context") {
40}
41
42Canvas::Canvas(const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size, float global_scale)
43 : renderer_(nullptr), canvas_id_(id), context_id_(id + "Context") {
49}
50
51// New constructors with renderer support (for migration to IRenderer pattern)
52Canvas::Canvas(gfx::IRenderer* renderer) : renderer_(renderer) { InitializeDefaults(); }
53
54Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id)
55 : renderer_(renderer), canvas_id_(id), context_id_(id + "Context") {
57}
58
59Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id, ImVec2 canvas_size)
60 : renderer_(renderer), canvas_id_(id), context_id_(id + "Context") {
64}
65
66Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size)
67 : renderer_(renderer), canvas_id_(id), context_id_(id + "Context") {
72}
73
74Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size, float global_scale)
75 : renderer_(renderer), canvas_id_(id), context_id_(id + "Context") {
81}
82
83Canvas::~Canvas() = default;
84
85using ImGui::GetContentRegionAvail;
86using ImGui::GetCursorScreenPos;
87using ImGui::GetIO;
88using ImGui::GetWindowDrawList;
89using ImGui::IsItemActive;
90using ImGui::IsItemHovered;
91using ImGui::IsMouseClicked;
92using ImGui::IsMouseDragging;
93using ImGui::Text;
94
95constexpr uint32_t kRectangleColor = IM_COL32(32, 32, 32, 255);
96constexpr uint32_t kWhiteColor = IM_COL32(255, 255, 255, 255);
97
98constexpr ImGuiButtonFlags kMouseFlags =
99 ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight;
100
101namespace {
102ImVec2 AlignPosToGrid(ImVec2 pos, float scale) {
103 return ImVec2(std::floor(pos.x / scale) * scale,
104 std::floor(pos.y / scale) * scale);
105}
106} // namespace
107
108// Canvas class implementation begins here
109
111 // Initialize configuration with sensible defaults
112 config_.enable_grid = true;
116 config_.is_draggable = false;
117 config_.grid_step = 32.0f;
118 config_.global_scale = 1.0f;
119 config_.canvas_size = ImVec2(0, 0);
122
123 // Initialize selection state
125
126 // Initialize palette editor
127 palette_editor_ = std::make_unique<PaletteWidget>();
128
129 // Initialize interaction handler
131
132 // Initialize enhanced components
134
135 // Initialize legacy compatibility variables to match config
146}
147
149 palette_editor_.reset();
151
152 // Stop performance monitoring before cleanup to prevent segfault
154 performance_integration_->StopMonitoring();
155 }
156
157 // Cleanup enhanced components
158 modals_.reset();
159 context_menu_.reset();
160 usage_tracker_.reset();
162}
163
165 // Initialize modals system
166 modals_ = std::make_unique<canvas::CanvasModals>();
167
168 // Initialize context menu system
169 context_menu_ = std::make_unique<canvas::CanvasContextMenu>();
170 context_menu_->Initialize(canvas_id_);
171
172 // Initialize usage tracker
173 usage_tracker_ = std::make_shared<canvas::CanvasUsageTracker>();
174 usage_tracker_->Initialize(canvas_id_);
176
177 // Initialize performance integration
179 std::make_shared<canvas::CanvasPerformanceIntegration>();
184
185 // Start performance monitoring
186 performance_integration_->StartMonitoring();
187 usage_tracker_->StartSession();
188}
189
191 if (usage_tracker_) {
192 usage_tracker_->SetUsageMode(usage);
193 }
194 if (context_menu_) {
195 context_menu_->SetUsageMode(usage);
196 }
197}
198
200 if (usage_tracker_) {
201 return usage_tracker_->GetCurrentStats().usage_mode;
202 }
204}
205
206void Canvas::RecordCanvasOperation(const std::string& operation_name,
207 double time_ms) {
208 if (usage_tracker_) {
209 usage_tracker_->RecordOperation(operation_name, time_ms);
210 }
212 performance_integration_->RecordOperation(operation_name, time_ms,
213 GetUsageMode());
214 }
215}
216
219 performance_integration_->RenderPerformanceUI();
220 }
221}
222
224 if (usage_tracker_) {
225 std::string report = usage_tracker_->ExportUsageReport();
226 // Show report in a modal or window
227 if (modals_) {
228 // Create a simple text display modal
229 ImGui::OpenPopup("Canvas Usage Report");
230 if (ImGui::BeginPopupModal("Canvas Usage Report", nullptr,
231 ImGuiWindowFlags_AlwaysAutoResize)) {
232 ImGui::Text("Canvas Usage Report");
233 ImGui::Separator();
234 ImGui::TextWrapped("%s", report.c_str());
235 ImGui::Separator();
236 if (ImGui::Button("Close")) {
237 ImGui::CloseCurrentPopup();
238 }
239 ImGui::EndPopup();
240 }
241 }
242 }
243}
244
246 rom_ = rom;
247 if (palette_editor_) {
248 palette_editor_->Initialize(rom);
249 }
250}
251
253 if (palette_editor_ && bitmap_) {
254 auto mutable_palette = bitmap_->mutable_palette();
255 palette_editor_->ShowPaletteEditor(*mutable_palette,
256 "Canvas Palette Editor");
257 }
258}
259
261 if (palette_editor_ && bitmap_) {
262 palette_editor_->ShowColorAnalysis(*bitmap_, "Canvas Color Analysis");
263 }
264}
265
266bool Canvas::ApplyROMPalette(int group_index, int palette_index) {
267 if (palette_editor_ && bitmap_) {
268 return palette_editor_->ApplyROMPalette(bitmap_, group_index, palette_index);
269 }
270 return false;
271}
272
273// Size reporting methods for table integration
278
283
284void Canvas::ReserveTableSpace(const std::string& label) {
287}
288
289bool Canvas::BeginTableCanvas(const std::string& label) {
290 if (config_.auto_resize) {
291 ImVec2 preferred_size = GetPreferredSize();
292 CanvasUtils::SetNextCanvasSize(preferred_size, true);
293 }
294
295 // Begin child window that properly reports size to tables
296 std::string child_id = canvas_id_ + "_TableChild";
297 ImVec2 child_size = config_.auto_resize ? ImVec2(0, 0) : config_.canvas_size;
298
299 bool result =
300 ImGui::BeginChild(child_id.c_str(), child_size,
301 true, // Always show border for table integration
302 ImGuiWindowFlags_AlwaysVerticalScrollbar);
303
304 if (!label.empty()) {
305 ImGui::Text("%s", label.c_str());
306 }
307
308 return result;
309}
310
312 ImGui::EndChild();
313}
314
315// Improved interaction detection methods
317 return !points_.empty() && points_.size() >= 2;
318}
319
320bool Canvas::WasClicked(ImGuiMouseButton button) const {
321 return ImGui::IsItemClicked(button) && HasValidSelection();
322}
323
324bool Canvas::WasDoubleClicked(ImGuiMouseButton button) const {
325 return ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(button) &&
327}
328
330 if (HasValidSelection()) {
331 return points_[0]; // Return the first point of the selection
332 }
333 return ImVec2(-1, -1); // Invalid position
334}
335
336// ==================== Modern ImGui-Style Interface ====================
337
338void Canvas::Begin(ImVec2 canvas_size) {
339 // Modern ImGui-style begin - combines DrawBackground + DrawContextMenu
342}
343
345 // Modern ImGui-style end - automatically draws grid and overlay
346 if (config_.enable_grid) {
347 DrawGrid();
348 }
349 DrawOverlay();
350
351 // Render any persistent popups from context menu actions
353}
354
355// ==================== Legacy Interface ====================
356
357void Canvas::UpdateColorPainter(gfx::IRenderer* /*renderer*/, gfx::Bitmap& bitmap, const ImVec4& color,
358 const std::function<void()>& event,
359 int tile_size, float scale) {
360 config_.global_scale = scale;
361 global_scale_ = scale; // Legacy compatibility
364 DrawBitmap(bitmap, 2, scale);
365 if (DrawSolidTilePainter(color, tile_size)) {
366 event();
367 bitmap.UpdateTexture();
368 }
369 DrawGrid();
370 DrawOverlay();
371}
372
373void Canvas::UpdateInfoGrid(ImVec2 bg_size, float grid_size, int label_id) {
375 enable_custom_labels_ = true; // Legacy compatibility
376 DrawBackground(bg_size);
377 DrawInfoGrid(grid_size, 8, label_id);
378 DrawOverlay();
379}
380
381void Canvas::DrawBackground(ImVec2 canvas_size) {
382 draw_list_ = GetWindowDrawList();
383 canvas_p0_ = GetCursorScreenPos();
384
385 // Calculate canvas size using utility function
386 ImVec2 content_region = GetContentRegionAvail();
389
390 if (canvas_size.x != 0) {
393 }
394
395 // Calculate scaled canvas bounds
396 ImVec2 scaled_size =
398
399 // CRITICAL FIX: Ensure minimum size to prevent ImGui assertions
400 if (scaled_size.x <= 0.0f)
401 scaled_size.x = 1.0f;
402 if (scaled_size.y <= 0.0f)
403 scaled_size.y = 1.0f;
404
405 canvas_p1_ =
406 ImVec2(canvas_p0_.x + scaled_size.x, canvas_p0_.y + scaled_size.y);
407
408 // Draw border and background color
411
412 ImGui::InvisibleButton(canvas_id_.c_str(), scaled_size, kMouseFlags);
413
414 // CRITICAL FIX: Always update hover mouse position when hovering over canvas
415 // This fixes the regression where CheckForCurrentMap() couldn't track hover
416 if (IsItemHovered()) {
417 const ImGuiIO& io = GetIO();
418 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
419 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
420 mouse_pos_in_canvas_ = mouse_pos;
421 }
422
423 if (config_.is_draggable && IsItemHovered()) {
424 const ImGuiIO& io = GetIO();
425 const bool is_active = IsItemActive(); // Held
426 const ImVec2 origin(canvas_p0_.x + scrolling_.x,
427 canvas_p0_.y + scrolling_.y); // Lock scrolled origin
428 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
429
430 // Pan (we use a zero mouse threshold when there's no context menu)
431 if (const float mouse_threshold_for_pan =
432 enable_context_menu_ ? -1.0f : 0.0f;
433 is_active &&
434 IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan)) {
435 scrolling_.x += io.MouseDelta.x;
436 scrolling_.y += io.MouseDelta.y;
437 }
438 }
439}
440
442 const ImGuiIO& io = GetIO();
443 const ImVec2 scaled_sz(canvas_sz_.x * global_scale_,
445 const ImVec2 origin(canvas_p0_.x + scrolling_.x,
446 canvas_p0_.y + scrolling_.y); // Lock scrolled origin
447 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
448
449 // Update canvas state for enhanced components
450 if (usage_tracker_) {
451 usage_tracker_->UpdateCanvasState(
454 }
455
456 // Use enhanced context menu if available
457 if (context_menu_) {
458 canvas::CanvasConfig snapshot;
459 snapshot.canvas_size = canvas_sz_;
461 snapshot.global_scale = global_scale_;
462 snapshot.grid_step = custom_step_;
463 snapshot.enable_grid = enable_grid_;
467 snapshot.is_draggable = draggable_;
469 snapshot.scrolling = scrolling_;
470
471 context_menu_->SetCanvasState(
475
476 context_menu_->Render(
477 context_id_, mouse_pos, rom_, bitmap_,
478 bitmap_ ? bitmap_->mutable_palette() : nullptr,
480 const canvas::CanvasConfig& updated_config) {
481 switch (command) {
483 ResetView();
484 break;
486 if (bitmap_) {
488 }
489 break;
492 break;
495 break;
499 break;
503 break;
507 break;
511 break;
514 break;
518 break;
520 config_.grid_step = updated_config.grid_step;
522 break;
524 config_.global_scale = updated_config.global_scale;
526 break;
528 if (modals_) {
529 canvas::CanvasConfig modal_config = updated_config;
530 modal_config.on_config_changed =
531 [this](const canvas::CanvasConfig& cfg) {
533 };
534 modal_config.on_scale_changed =
535 [this](const canvas::CanvasConfig& cfg) {
537 };
538 modals_->ShowAdvancedProperties(canvas_id_, modal_config,
539 bitmap_);
540 }
541 break;
543 if (modals_) {
544 canvas::CanvasConfig modal_config = updated_config;
545 modal_config.on_config_changed =
546 [this](const canvas::CanvasConfig& cfg) {
548 };
549 modal_config.on_scale_changed =
550 [this](const canvas::CanvasConfig& cfg) {
552 };
553 modals_->ShowScalingControls(canvas_id_, modal_config, bitmap_);
554 }
555 break;
556 default:
557 break;
558 }
559 },
560 snapshot);
561
562 if (modals_) {
563 modals_->Render();
564 }
565
566 // CRITICAL: Render custom context menu items AFTER enhanced menu
567 // Don't return early - we need to show custom items too!
568 if (!context_menu_items_.empty() && ImGui::BeginPopupContextItem(context_id_.c_str())) {
569 for (const auto& item : context_menu_items_) {
571 }
572 ImGui::EndPopup();
573 }
574
575 return;
576 }
577
578
579
580 // Draw enhanced property dialogs
583}
584
586 if (!item.enabled_condition()) {
587 ImGui::BeginDisabled();
588 }
589
590 if (item.subitems.empty()) {
591 // Simple menu item
592 if (ImGui::MenuItem(item.label.c_str(), item.shortcut.empty()
593 ? nullptr
594 : item.shortcut.c_str())) {
595 item.callback();
596 }
597 } else {
598 // Menu with subitems
599 if (ImGui::BeginMenu(item.label.c_str())) {
600 for (const auto& subitem : item.subitems) {
601 DrawContextMenuItem(subitem);
602 }
603 ImGui::EndMenu();
604 }
605 }
606
607 if (!item.enabled_condition()) {
608 ImGui::EndDisabled();
609 }
610}
611
613 context_menu_items_.push_back(item);
614}
615
619
620void Canvas::OpenPersistentPopup(const std::string& popup_id, std::function<void()> render_callback) {
621 // Check if popup already exists
622 for (auto& popup : active_popups_) {
623 if (popup.popup_id == popup_id) {
624 popup.is_open = true;
625 popup.render_callback = std::move(render_callback);
626 ImGui::OpenPopup(popup_id.c_str());
627 return;
628 }
629 }
630
631 // Add new popup
632 PopupState new_popup;
633 new_popup.popup_id = popup_id;
634 new_popup.is_open = true;
635 new_popup.render_callback = std::move(render_callback);
636 active_popups_.push_back(new_popup);
637 ImGui::OpenPopup(popup_id.c_str());
638}
639
640void Canvas::ClosePersistentPopup(const std::string& popup_id) {
641 for (auto& popup : active_popups_) {
642 if (popup.popup_id == popup_id) {
643 popup.is_open = false;
644 ImGui::CloseCurrentPopup();
645 return;
646 }
647 }
648}
649
651 // Render all active popups
652 auto it = active_popups_.begin();
653 while (it != active_popups_.end()) {
654 if (it->is_open && it->render_callback) {
655 // Call the render callback which should handle BeginPopup/EndPopup
656 it->render_callback();
657
658 // If popup was closed by user, mark it for removal
659 if (!ImGui::IsPopupOpen(it->popup_id.c_str())) {
660 it->is_open = false;
661 }
662 }
663
664 // Remove closed popups
665 if (!it->is_open) {
666 it = active_popups_.erase(it);
667 } else {
668 ++it;
669 }
670 }
671}
672
674 if (!bitmap.is_active())
675 return;
676
677 ImVec2 available = ImGui::GetContentRegionAvail();
678 float scale_x = available.x / bitmap.width();
679 float scale_y = available.y / bitmap.height();
680 config_.global_scale = std::min(scale_x, scale_y);
681
682 // Ensure minimum readable scale
683 if (config_.global_scale < 0.25f)
684 config_.global_scale = 0.25f;
685
686 global_scale_ = config_.global_scale; // Legacy compatibility
687
688 // Center the view
689 scrolling_ = ImVec2(0, 0);
690}
691
693 config_.global_scale = 1.0f;
694 global_scale_ = 1.0f; // Legacy compatibility
695 scrolling_ = ImVec2(0, 0);
696}
697
721
727
728bool Canvas::DrawTilePainter(const Bitmap& bitmap, int size, float scale) {
729 const ImGuiIO& io = GetIO();
730 const bool is_hovered = IsItemHovered();
731 is_hovered_ = is_hovered;
732 // Lock scrolled origin
733 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
734 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
735 const auto scaled_size = size * scale;
736
737 // Erase the hover when the mouse is not in the canvas window.
738 if (!is_hovered) {
739 points_.clear();
740 return false;
741 }
742
743 // Reset the previous tile hover
744 if (!points_.empty()) {
745 points_.clear();
746 }
747
748 // Calculate the coordinates of the mouse
749 ImVec2 paint_pos = AlignPosToGrid(mouse_pos, scaled_size);
750 mouse_pos_in_canvas_ = paint_pos;
751 auto paint_pos_end =
752 ImVec2(paint_pos.x + scaled_size, paint_pos.y + scaled_size);
753 points_.push_back(paint_pos);
754 points_.push_back(paint_pos_end);
755
756 if (bitmap.is_active()) {
757 draw_list_->AddImage((ImTextureID)(intptr_t)bitmap.texture(),
758 ImVec2(origin.x + paint_pos.x, origin.y + paint_pos.y),
759 ImVec2(origin.x + paint_pos.x + scaled_size,
760 origin.y + paint_pos.y + scaled_size));
761 }
762
763 if (IsMouseClicked(ImGuiMouseButton_Left) &&
764 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
765 // Draw the currently selected tile on the overworld here
766 // Save the coordinates of the selected tile.
767 drawn_tile_pos_ = paint_pos;
768 return true;
769 }
770
771 return false;
772}
773
774bool Canvas::DrawTilemapPainter(gfx::Tilemap& tilemap, int current_tile) {
775 const ImGuiIO& io = GetIO();
776 const bool is_hovered = IsItemHovered();
777 is_hovered_ = is_hovered;
778 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
779 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
780
781 // Safety check: ensure tilemap is properly initialized
782 if (!tilemap.atlas.is_active() || tilemap.tile_size.x <= 0) {
783 return false;
784 }
785
786 const auto scaled_size = tilemap.tile_size.x * global_scale_;
787
788 if (!is_hovered) {
789 points_.clear();
790 return false;
791 }
792
793 if (!points_.empty()) {
794 points_.clear();
795 }
796
797 ImVec2 paint_pos = AlignPosToGrid(mouse_pos, scaled_size);
798 mouse_pos_in_canvas_ = paint_pos;
799
800 points_.push_back(paint_pos);
801 points_.push_back(
802 ImVec2(paint_pos.x + scaled_size, paint_pos.y + scaled_size));
803
804 // CRITICAL FIX: Disable tile cache system to prevent crashes
805 // Just draw a simple preview tile using the atlas directly
806 if (tilemap.atlas.is_active() && tilemap.atlas.texture()) {
807 // Draw the tile directly from the atlas without caching
808 int tiles_per_row = tilemap.atlas.width() / tilemap.tile_size.x;
809 if (tiles_per_row > 0) {
810 int tile_x = (current_tile % tiles_per_row) * tilemap.tile_size.x;
811 int tile_y = (current_tile / tiles_per_row) * tilemap.tile_size.y;
812
813 // Simple bounds check
814 if (tile_x >= 0 && tile_x < tilemap.atlas.width() && tile_y >= 0 &&
815 tile_y < tilemap.atlas.height()) {
816
817 // Draw directly from atlas texture
818 ImVec2 uv0 =
819 ImVec2(static_cast<float>(tile_x) / tilemap.atlas.width(),
820 static_cast<float>(tile_y) / tilemap.atlas.height());
821 ImVec2 uv1 = ImVec2(static_cast<float>(tile_x + tilemap.tile_size.x) /
822 tilemap.atlas.width(),
823 static_cast<float>(tile_y + tilemap.tile_size.y) /
824 tilemap.atlas.height());
825
826 draw_list_->AddImage(
827 (ImTextureID)(intptr_t)tilemap.atlas.texture(),
828 ImVec2(origin.x + paint_pos.x, origin.y + paint_pos.y),
829 ImVec2(origin.x + paint_pos.x + scaled_size,
830 origin.y + paint_pos.y + scaled_size),
831 uv0, uv1);
832 }
833 }
834 }
835
836 if (IsMouseClicked(ImGuiMouseButton_Left) ||
837 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
838 drawn_tile_pos_ = paint_pos;
839 return true;
840 }
841
842 return false;
843}
844
845bool Canvas::DrawSolidTilePainter(const ImVec4& color, int tile_size) {
846 const ImGuiIO& io = GetIO();
847 const bool is_hovered = IsItemHovered();
848 is_hovered_ = is_hovered;
849 // Lock scrolled origin
850 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
851 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
852 auto scaled_tile_size = tile_size * global_scale_;
853 static bool is_dragging = false;
854 static ImVec2 start_drag_pos;
855
856 // Erase the hover when the mouse is not in the canvas window.
857 if (!is_hovered) {
858 points_.clear();
859 return false;
860 }
861
862 // Reset the previous tile hover
863 if (!points_.empty()) {
864 points_.clear();
865 }
866
867 // Calculate the coordinates of the mouse
868 ImVec2 paint_pos = AlignPosToGrid(mouse_pos, scaled_tile_size);
869 mouse_pos_in_canvas_ = paint_pos;
870
871 // Clamp the size to a grid
872 paint_pos.x = std::clamp(paint_pos.x, 0.0f, canvas_sz_.x * global_scale_);
873 paint_pos.y = std::clamp(paint_pos.y, 0.0f, canvas_sz_.y * global_scale_);
874
875 points_.push_back(paint_pos);
876 points_.push_back(
877 ImVec2(paint_pos.x + scaled_tile_size, paint_pos.y + scaled_tile_size));
878
879 draw_list_->AddRectFilled(
880 ImVec2(origin.x + paint_pos.x + 1, origin.y + paint_pos.y + 1),
881 ImVec2(origin.x + paint_pos.x + scaled_tile_size,
882 origin.y + paint_pos.y + scaled_tile_size),
883 IM_COL32(color.x * 255, color.y * 255, color.z * 255, 255));
884
885 if (IsMouseClicked(ImGuiMouseButton_Left)) {
886 is_dragging = true;
887 start_drag_pos = paint_pos;
888 }
889
890 if (is_dragging && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
891 is_dragging = false;
892 drawn_tile_pos_ = start_drag_pos;
893 return true;
894 }
895
896 return false;
897}
898
899void Canvas::DrawTileOnBitmap(int tile_size, gfx::Bitmap* bitmap,
900 ImVec4 color) {
901 const ImVec2 position = drawn_tile_pos_;
902 int tile_index_x = static_cast<int>(position.x / global_scale_) / tile_size;
903 int tile_index_y = static_cast<int>(position.y / global_scale_) / tile_size;
904
905 ImVec2 start_position(tile_index_x * tile_size, tile_index_y * tile_size);
906
907 // Update the bitmap's pixel data based on the start_position and color
908 for (int y = 0; y < tile_size; ++y) {
909 for (int x = 0; x < tile_size; ++x) {
910 // Calculate the actual pixel index in the bitmap
911 int pixel_index =
912 (start_position.y + y) * bitmap->width() + (start_position.x + x);
913
914 // Write the color to the pixel
915 bitmap->WriteColor(pixel_index, color);
916 }
917 }
918}
919
920bool Canvas::DrawTileSelector(int size, int size_y) {
921 const ImGuiIO& io = GetIO();
922 const bool is_hovered = IsItemHovered();
923 is_hovered_ = is_hovered;
924 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
925 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
926 if (size_y == 0) {
927 size_y = size;
928 }
929
930 if (is_hovered && IsMouseClicked(ImGuiMouseButton_Left)) {
931 if (!points_.empty()) {
932 points_.clear();
933 }
934 ImVec2 painter_pos = AlignPosToGrid(mouse_pos, size);
935
936 points_.push_back(painter_pos);
937 points_.push_back(ImVec2(painter_pos.x + size, painter_pos.y + size_y));
938 mouse_pos_in_canvas_ = painter_pos;
939 }
940
941 if (is_hovered && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
942 return true;
943 }
944
945 return false;
946}
947
948void Canvas::DrawSelectRect(int current_map, int tile_size, float scale) {
949 gfx::ScopedTimer timer("canvas_select_rect");
950
951 const ImGuiIO& io = GetIO();
952 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
953 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
954 static ImVec2 drag_start_pos;
955 const float scaled_size = tile_size * scale;
956 static bool dragging = false;
957 constexpr int small_map_size = 0x200;
958
959 // Only handle mouse events if the canvas is hovered
960 const bool is_hovered = IsItemHovered();
961 if (!is_hovered) {
962 return;
963 }
964
965 // Calculate superX and superY accounting for world offset
966 int superY, superX;
967 if (current_map < 0x40) {
968 // Light World
969 superY = current_map / 8;
970 superX = current_map % 8;
971 } else if (current_map < 0x80) {
972 // Dark World
973 superY = (current_map - 0x40) / 8;
974 superX = (current_map - 0x40) % 8;
975 } else {
976 // Special World
977 superY = (current_map - 0x80) / 8;
978 superX = (current_map - 0x80) % 8;
979 }
980
981 // Handle right click for single tile selection
982 if (IsMouseClicked(ImGuiMouseButton_Right)) {
983 ImVec2 painter_pos = AlignPosToGrid(mouse_pos, scaled_size);
984 int painter_x = painter_pos.x;
985 int painter_y = painter_pos.y;
986
987 auto tile16_x = (painter_x % small_map_size) / (small_map_size / 0x20);
988 auto tile16_y = (painter_y % small_map_size) / (small_map_size / 0x20);
989
990 int index_x = superX * 0x20 + tile16_x;
991 int index_y = superY * 0x20 + tile16_y;
992 selected_tile_pos_ = ImVec2(index_x, index_y);
993 selected_points_.clear();
994 select_rect_active_ = false;
995
996 // Start drag position for rectangle selection
997 drag_start_pos = AlignPosToGrid(mouse_pos, scaled_size);
998 }
999
1000 // Calculate the rectangle's top-left and bottom-right corners
1001 ImVec2 drag_end_pos = AlignPosToGrid(mouse_pos, scaled_size);
1002 if (ImGui::IsMouseDragging(ImGuiMouseButton_Right)) {
1003 // FIX: Origin used to be canvas_p0_, revert if there is regression.
1004 auto start = ImVec2(origin.x + drag_start_pos.x,
1005 origin.y + drag_start_pos.y);
1006 auto end = ImVec2(origin.x + drag_end_pos.x + tile_size,
1007 origin.y + drag_end_pos.y + tile_size);
1008 draw_list_->AddRect(start, end, kWhiteColor);
1009 dragging = true;
1010 }
1011
1012 if (dragging && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
1013 // Release dragging mode
1014 dragging = false;
1015
1016 // Calculate the bounds of the rectangle in terms of 16x16 tile indices
1017 constexpr int tile16_size = 16;
1018 int start_x = std::floor(drag_start_pos.x / scaled_size) * tile16_size;
1019 int start_y = std::floor(drag_start_pos.y / scaled_size) * tile16_size;
1020 int end_x = std::floor(drag_end_pos.x / scaled_size) * tile16_size;
1021 int end_y = std::floor(drag_end_pos.y / scaled_size) * tile16_size;
1022
1023 // Swap the start and end positions if they are in the wrong order
1024 if (start_x > end_x)
1025 std::swap(start_x, end_x);
1026 if (start_y > end_y)
1027 std::swap(start_y, end_y);
1028
1029 selected_tiles_.clear();
1030 selected_tiles_.reserve(((end_x - start_x) / tile16_size + 1) *
1031 ((end_y - start_y) / tile16_size + 1));
1032
1033 // Number of tiles per local map (since each tile is 16x16)
1034 constexpr int tiles_per_local_map = small_map_size / 16;
1035
1036 // Loop through the tiles in the rectangle and store their positions
1037 for (int y = start_y; y <= end_y; y += tile16_size) {
1038 for (int x = start_x; x <= end_x; x += tile16_size) {
1039 // Determine which local map (512x512) the tile is in
1040 int local_map_x = (x / small_map_size) % 8;
1041 int local_map_y = (y / small_map_size) % 8;
1042
1043 // Calculate the tile's position within its local map
1044 int tile16_x = (x % small_map_size) / tile16_size;
1045 int tile16_y = (y % small_map_size) / tile16_size;
1046
1047 // Calculate the index within the overall map structure
1048 int index_x = local_map_x * tiles_per_local_map + tile16_x;
1049 int index_y = local_map_y * tiles_per_local_map + tile16_y;
1050
1051 selected_tiles_.emplace_back(index_x, index_y);
1052 }
1053 }
1054 // Clear and add the calculated rectangle points
1055 selected_points_.clear();
1056 selected_points_.push_back(drag_start_pos);
1057 selected_points_.push_back(drag_end_pos);
1058 select_rect_active_ = true;
1059 }
1060}
1061
1062void Canvas::DrawBitmap(Bitmap& bitmap, int /*border_offset*/, float scale) {
1063 if (!bitmap.is_active()) {
1064 return;
1065 }
1066 bitmap_ = &bitmap;
1067
1068 // Update content size for table integration
1069 config_.content_size = ImVec2(bitmap.width(), bitmap.height());
1070
1071 draw_list_->AddImage((ImTextureID)(intptr_t)bitmap.texture(),
1072 ImVec2(canvas_p0_.x, canvas_p0_.y),
1073 ImVec2(canvas_p0_.x + (bitmap.width() * scale),
1074 canvas_p0_.y + (bitmap.height() * scale)));
1076}
1077
1078void Canvas::DrawBitmap(Bitmap& bitmap, int x_offset, int y_offset, float scale,
1079 int alpha) {
1080 if (!bitmap.is_active()) {
1081 return;
1082 }
1083 bitmap_ = &bitmap;
1084
1085 // Update content size for table integration
1086 // CRITICAL: Store UNSCALED bitmap size as content - scale is applied during rendering
1087 config_.content_size = ImVec2(bitmap.width(), bitmap.height());
1088
1089 // Calculate the actual rendered size including scale and offsets
1090 // CRITICAL: Use scale parameter (NOT global_scale_) for per-bitmap scaling
1091 ImVec2 rendered_size(bitmap.width() * scale, bitmap.height() * scale);
1092 ImVec2 total_size(x_offset + rendered_size.x, y_offset + rendered_size.y);
1093
1094 // CRITICAL FIX: Draw bitmap WITHOUT additional global_scale multiplication
1095 // The scale parameter already contains the correct scale factor
1096 // The scrolling should NOT be scaled - it's already in screen space
1097 draw_list_->AddImage(
1098 (ImTextureID)(intptr_t)bitmap.texture(),
1099 ImVec2(canvas_p0_.x + x_offset + scrolling_.x,
1100 canvas_p0_.y + y_offset + scrolling_.y),
1101 ImVec2(canvas_p0_.x + x_offset + scrolling_.x + rendered_size.x,
1102 canvas_p0_.y + y_offset + scrolling_.y + rendered_size.y),
1103 ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, alpha));
1104
1105 // Note: Content size for child windows should be set before BeginChild, not here
1106}
1107
1108void Canvas::DrawBitmap(Bitmap& bitmap, ImVec2 dest_pos, ImVec2 dest_size,
1109 ImVec2 src_pos, ImVec2 src_size) {
1110 if (!bitmap.is_active()) {
1111 return;
1112 }
1113 bitmap_ = &bitmap;
1114
1115 // Update content size for table integration
1116 config_.content_size = ImVec2(bitmap.width(), bitmap.height());
1117
1118 draw_list_->AddImage(
1119 (ImTextureID)(intptr_t)bitmap.texture(),
1120 ImVec2(canvas_p0_.x + dest_pos.x, canvas_p0_.y + dest_pos.y),
1121 ImVec2(canvas_p0_.x + dest_pos.x + dest_size.x,
1122 canvas_p0_.y + dest_pos.y + dest_size.y),
1123 ImVec2(src_pos.x / bitmap.width(), src_pos.y / bitmap.height()),
1124 ImVec2((src_pos.x + src_size.x) / bitmap.width(),
1125 (src_pos.y + src_size.y) / bitmap.height()));
1126}
1127
1128// TODO: Add parameters for sizing and positioning
1129void Canvas::DrawBitmapTable(const BitmapTable& gfx_bin) {
1130 for (const auto& [key, value] : gfx_bin) {
1131 int offset = 0x40 * (key + 1);
1132 int top_left_y = canvas_p0_.y + 2;
1133 if (key >= 1) {
1134 top_left_y = canvas_p0_.y + 0x40 * key;
1135 }
1136 draw_list_->AddImage((ImTextureID)(intptr_t)value.texture(),
1137 ImVec2(canvas_p0_.x + 2, top_left_y),
1138 ImVec2(canvas_p0_.x + 0x100, canvas_p0_.y + offset));
1139 }
1140}
1141
1142void Canvas::DrawOutline(int x, int y, int w, int h) {
1144 IM_COL32(255, 255, 255, 200));
1145}
1146
1147void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color) {
1149 y, w, h, color);
1150}
1151
1152void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, uint32_t color) {
1154 color);
1155}
1156
1157void Canvas::DrawBitmapGroup(std::vector<int>& group, gfx::Tilemap& tilemap,
1158 int tile_size, float scale, int local_map_size,
1159 ImVec2 total_map_size) {
1160 if (selected_points_.size() != 2) {
1161 // points_ should contain exactly two points
1162 return;
1163 }
1164 if (group.empty()) {
1165 // group should not be empty
1166 return;
1167 }
1168
1169 // OPTIMIZATION: Use optimized rendering for large groups to improve performance
1170 bool use_optimized_rendering =
1171 group.size() > 128; // Optimize for large selections
1172
1173 // Use provided map sizes for proper boundary handling
1174 const int small_map = local_map_size;
1175 const float large_map_width = total_map_size.x;
1176 const float large_map_height = total_map_size.y;
1177
1178 // Pre-calculate common values to avoid repeated computation
1179 const float tile_scale = tile_size * scale;
1180 const int atlas_tiles_per_row = tilemap.atlas.width() / tilemap.tile_size.x;
1181
1182 // Top-left and bottom-right corners of the rectangle
1183 ImVec2 rect_top_left = selected_points_[0];
1184 ImVec2 rect_bottom_right = selected_points_[1];
1185
1186 // Calculate the start and end tiles in the grid
1187 int start_tile_x =
1188 static_cast<int>(std::floor(rect_top_left.x / (tile_size * scale)));
1189 int start_tile_y =
1190 static_cast<int>(std::floor(rect_top_left.y / (tile_size * scale)));
1191 int end_tile_x =
1192 static_cast<int>(std::floor(rect_bottom_right.x / (tile_size * scale)));
1193 int end_tile_y =
1194 static_cast<int>(std::floor(rect_bottom_right.y / (tile_size * scale)));
1195
1196 if (start_tile_x > end_tile_x)
1197 std::swap(start_tile_x, end_tile_x);
1198 if (start_tile_y > end_tile_y)
1199 std::swap(start_tile_y, end_tile_y);
1200
1201 // Calculate the size of the rectangle in 16x16 grid form
1202 int rect_width = (end_tile_x - start_tile_x) * tile_size;
1203 int rect_height = (end_tile_y - start_tile_y) * tile_size;
1204
1205 int tiles_per_row = rect_width / tile_size;
1206 int tiles_per_col = rect_height / tile_size;
1207
1208 int i = 0;
1209 for (int y = 0; y < tiles_per_col + 1; ++y) {
1210 for (int x = 0; x < tiles_per_row + 1; ++x) {
1211 // Check bounds to prevent access violations
1212 if (i >= static_cast<int>(group.size())) {
1213 break;
1214 }
1215
1216 int tile_id = group[i];
1217
1218 // Check if tile_id is within the range of tile16_individual_
1219 auto tilemap_size = tilemap.map_size.x;
1220 if (tile_id >= 0 && tile_id < tilemap_size) {
1221 // Calculate the position of the tile within the rectangle
1222 int tile_pos_x = (x + start_tile_x) * tile_size * scale;
1223 int tile_pos_y = (y + start_tile_y) * tile_size * scale;
1224
1225 // OPTIMIZATION: Use pre-calculated values for better performance with large selections
1226 if (tilemap.atlas.is_active() && tilemap.atlas.texture() &&
1227 atlas_tiles_per_row > 0) {
1228 int atlas_tile_x =
1229 (tile_id % atlas_tiles_per_row) * tilemap.tile_size.x;
1230 int atlas_tile_y =
1231 (tile_id / atlas_tiles_per_row) * tilemap.tile_size.y;
1232
1233 // Simple bounds check
1234 if (atlas_tile_x >= 0 && atlas_tile_x < tilemap.atlas.width() &&
1235 atlas_tile_y >= 0 && atlas_tile_y < tilemap.atlas.height()) {
1236
1237 // Calculate UV coordinates once for efficiency
1238 const float atlas_width = static_cast<float>(tilemap.atlas.width());
1239 const float atlas_height =
1240 static_cast<float>(tilemap.atlas.height());
1241 ImVec2 uv0 =
1242 ImVec2(atlas_tile_x / atlas_width, atlas_tile_y / atlas_height);
1243 ImVec2 uv1 =
1244 ImVec2((atlas_tile_x + tilemap.tile_size.x) / atlas_width,
1245 (atlas_tile_y + tilemap.tile_size.y) / atlas_height);
1246
1247 // Calculate screen positions
1248 float screen_x = canvas_p0_.x + scrolling_.x + tile_pos_x;
1249 float screen_y = canvas_p0_.y + scrolling_.y + tile_pos_y;
1250 float screen_w = tilemap.tile_size.x * scale;
1251 float screen_h = tilemap.tile_size.y * scale;
1252
1253 // Use higher alpha for large selections to make them more visible
1254 uint32_t alpha_color = use_optimized_rendering
1255 ? IM_COL32(255, 255, 255, 200)
1256 : IM_COL32(255, 255, 255, 150);
1257
1258 // Draw from atlas texture with optimized parameters
1259 draw_list_->AddImage(
1260 (ImTextureID)(intptr_t)tilemap.atlas.texture(),
1261 ImVec2(screen_x, screen_y),
1262 ImVec2(screen_x + screen_w, screen_y + screen_h), uv0, uv1,
1263 alpha_color);
1264 }
1265 }
1266 }
1267 i++;
1268 }
1269 // Break outer loop if we've run out of tiles
1270 if (i >= static_cast<int>(group.size())) {
1271 break;
1272 }
1273 }
1274
1275 // Performance optimization completed - tiles are now rendered with pre-calculated values
1276
1277 // Reposition rectangle to follow mouse, but clamp to prevent wrapping across map boundaries
1278 const ImGuiIO& io = GetIO();
1279 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
1280 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
1281
1282 // CRITICAL FIX: Clamp BEFORE grid alignment for smoother dragging behavior
1283 // This prevents the rectangle from even attempting to cross boundaries during drag
1284 ImVec2 clamped_mouse_pos = mouse_pos;
1285
1287 // Calculate which local map the mouse is in
1288 int mouse_local_map_x = static_cast<int>(mouse_pos.x) / small_map;
1289 int mouse_local_map_y = static_cast<int>(mouse_pos.y) / small_map;
1290
1291 // Calculate where the rectangle END would be if we place it at mouse position
1292 float potential_end_x = mouse_pos.x + rect_width;
1293 float potential_end_y = mouse_pos.y + rect_height;
1294
1295 // Check if this would cross local map boundary (512x512 blocks)
1296 int potential_end_map_x = static_cast<int>(potential_end_x) / small_map;
1297 int potential_end_map_y = static_cast<int>(potential_end_y) / small_map;
1298
1299 // Clamp mouse position to prevent crossing during drag
1300 if (potential_end_map_x != mouse_local_map_x) {
1301 // Would cross horizontal boundary - clamp mouse to safe zone
1302 float max_mouse_x = (mouse_local_map_x + 1) * small_map - rect_width;
1303 clamped_mouse_pos.x = std::min(mouse_pos.x, max_mouse_x);
1304 }
1305
1306 if (potential_end_map_y != mouse_local_map_y) {
1307 // Would cross vertical boundary - clamp mouse to safe zone
1308 float max_mouse_y = (mouse_local_map_y + 1) * small_map - rect_height;
1309 clamped_mouse_pos.y = std::min(mouse_pos.y, max_mouse_y);
1310 }
1311 }
1312
1313 // Now grid-align the clamped position
1314 auto new_start_pos = AlignPosToGrid(clamped_mouse_pos, tile_size * scale);
1315
1316 // Additional safety: clamp to overall map bounds
1317 new_start_pos.x =
1318 std::clamp(new_start_pos.x, 0.0f, large_map_width - rect_width);
1319 new_start_pos.y =
1320 std::clamp(new_start_pos.y, 0.0f, large_map_height - rect_height);
1321
1322 selected_points_.clear();
1323 selected_points_.push_back(new_start_pos);
1324 selected_points_.push_back(
1325 ImVec2(new_start_pos.x + rect_width, new_start_pos.y + rect_height));
1326 select_rect_active_ = true;
1327}
1328
1329void Canvas::DrawRect(int x, int y, int w, int h, ImVec4 color) {
1331 color, config_.global_scale);
1332}
1333
1334void Canvas::DrawText(const std::string& text, int x, int y) {
1337}
1338
1343
1344void Canvas::DrawInfoGrid(float grid_step, int tile_id_offset, int label_id) {
1345 // Draw grid + all lines in the canvas
1346 draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
1347 if (enable_grid_) {
1348 if (custom_step_ != 0.f)
1349 grid_step = custom_step_;
1350 grid_step *= global_scale_; // Apply global scale to grid step
1351
1352 DrawGridLines(grid_step);
1353 DrawCustomHighlight(grid_step);
1354
1355 if (!enable_custom_labels_) {
1356 return;
1357 }
1358
1359 // Draw the contents of labels on the grid
1360 for (float x = fmodf(scrolling_.x, grid_step);
1361 x < canvas_sz_.x * global_scale_; x += grid_step) {
1362 for (float y = fmodf(scrolling_.y, grid_step);
1363 y < canvas_sz_.y * global_scale_; y += grid_step) {
1364 int tile_x = (x - scrolling_.x) / grid_step;
1365 int tile_y = (y - scrolling_.y) / grid_step;
1366 int tile_id = tile_x + (tile_y * tile_id_offset);
1367
1368 if (tile_id >= labels_[label_id].size()) {
1369 break;
1370 }
1371 std::string label = labels_[label_id][tile_id];
1372 draw_list_->AddText(
1373 ImVec2(canvas_p0_.x + x + (grid_step / 2) - tile_id_offset,
1374 canvas_p0_.y + y + (grid_step / 2) - tile_id_offset),
1375 kWhiteColor, label.data());
1376 }
1377 }
1378 }
1379}
1380
1385
1386void Canvas::DrawGrid(float grid_step, int tile_id_offset) {
1387 if (config_.grid_step != 0.f)
1388 grid_step = config_.grid_step;
1389
1390 // Create render context for utilities
1393 .canvas_p0 = canvas_p0_,
1394 .canvas_p1 = canvas_p1_,
1395 .scrolling = scrolling_,
1396 .global_scale = config_.global_scale,
1397 .enable_grid = config_.enable_grid,
1398 .enable_hex_labels = config_.enable_hex_labels,
1399 .grid_step = grid_step};
1400
1401 // Use high-level utility function
1403
1404 // Draw custom labels if enabled
1406 draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
1408 tile_id_offset);
1409 draw_list_->PopClipRect();
1410 }
1411}
1412
1414 // Create render context for utilities
1417 .canvas_p0 = canvas_p0_,
1418 .canvas_p1 = canvas_p1_,
1419 .scrolling = scrolling_,
1420 .global_scale = config_.global_scale,
1421 .enable_grid = config_.enable_grid,
1422 .enable_hex_labels = config_.enable_hex_labels,
1423 .grid_step = config_.grid_step};
1424
1425 // Use high-level utility function with local points (synchronized from interaction handler)
1427
1428 // Render any persistent popups from context menu actions
1430}
1431
1433 // Based on ImGui demo, should be adapted to use for OAM
1434 ImDrawList* draw_list = ImGui::GetWindowDrawList();
1435 {
1436 Text("Blue shape is drawn first: appears in back");
1437 Text("Red shape is drawn after: appears in front");
1438 ImVec2 p0 = ImGui::GetCursorScreenPos();
1439 draw_list->AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50),
1440 IM_COL32(0, 0, 255, 255)); // Blue
1441 draw_list->AddRectFilled(ImVec2(p0.x + 25, p0.y + 25),
1442 ImVec2(p0.x + 75, p0.y + 75),
1443 IM_COL32(255, 0, 0, 255)); // Red
1444 ImGui::Dummy(ImVec2(75, 75));
1445 }
1446 ImGui::Separator();
1447 {
1448 Text("Blue shape is drawn first, into channel 1: appears in front");
1449 Text("Red shape is drawn after, into channel 0: appears in back");
1450 ImVec2 p1 = ImGui::GetCursorScreenPos();
1451
1452 // Create 2 channels and draw a Blue shape THEN a Red shape.
1453 // You can create any number of channels. Tables API use 1 channel per
1454 // column in order to better batch draw calls.
1455 draw_list->ChannelsSplit(2);
1456 draw_list->ChannelsSetCurrent(1);
1457 draw_list->AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50),
1458 IM_COL32(0, 0, 255, 255)); // Blue
1459 draw_list->ChannelsSetCurrent(0);
1460 draw_list->AddRectFilled(ImVec2(p1.x + 25, p1.y + 25),
1461 ImVec2(p1.x + 75, p1.y + 75),
1462 IM_COL32(255, 0, 0, 255)); // Red
1463
1464 // Flatten/reorder channels. Red shape is in channel 0 and it appears
1465 // below the Blue shape in channel 1. This works by copying draw indices
1466 // only (vertices are not copied).
1467 draw_list->ChannelsMerge();
1468 ImGui::Dummy(ImVec2(75, 75));
1469 Text("After reordering, contents of channel 0 appears below channel 1.");
1470 }
1471}
1472
1473void BeginCanvas(Canvas& canvas, ImVec2 child_size) {
1475
1476 // Use improved canvas sizing for table integration
1477 ImVec2 effective_size = child_size;
1478 if (child_size.x == 0 && child_size.y == 0) {
1479 // Auto-size based on canvas configuration
1480 if (canvas.IsAutoResize()) {
1481 effective_size = canvas.GetPreferredSize();
1482 } else {
1483 effective_size = canvas.GetCurrentSize();
1484 }
1485 }
1486
1487 ImGui::BeginChild(canvas.canvas_id().c_str(), effective_size, true,
1488 ImGuiWindowFlags_AlwaysVerticalScrollbar);
1489 canvas.DrawBackground();
1491 canvas.DrawContextMenu();
1492}
1493
1494void EndCanvas(Canvas& canvas) {
1495 canvas.DrawGrid();
1496 canvas.DrawOverlay();
1497 ImGui::EndChild();
1498}
1499
1500void GraphicsBinCanvasPipeline(int width, int height, int tile_size,
1501 int num_sheets_to_load, int canvas_id,
1502 bool is_loaded, gfx::BitmapTable& graphics_bin) {
1503 gui::Canvas canvas;
1504 if (ImGuiID child_id =
1505 ImGui::GetID((ImTextureID)(intptr_t)(intptr_t)canvas_id);
1506 ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
1507 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
1508 canvas.DrawBackground(ImVec2(width + 1, num_sheets_to_load * height + 1));
1509 canvas.DrawContextMenu();
1510 if (is_loaded) {
1511 for (const auto& [key, value] : graphics_bin) {
1512 int offset = height * (key + 1);
1513 int top_left_y = canvas.zero_point().y + 2;
1514 if (key >= 1) {
1515 top_left_y = canvas.zero_point().y + height * key;
1516 }
1517 canvas.draw_list()->AddImage(
1518 (ImTextureID)(intptr_t)value.texture(),
1519 ImVec2(canvas.zero_point().x + 2, top_left_y),
1520 ImVec2(canvas.zero_point().x + 0x100,
1521 canvas.zero_point().y + offset));
1522 }
1523 }
1524 canvas.DrawTileSelector(tile_size);
1525 canvas.DrawGrid(tile_size);
1526 canvas.DrawOverlay();
1527 }
1528 ImGui::EndChild();
1529}
1530
1531void BitmapCanvasPipeline(gui::Canvas& canvas, gfx::Bitmap& bitmap, int width,
1532 int height, int tile_size, bool is_loaded,
1533 bool scrollbar, int canvas_id) {
1534 auto draw_canvas = [&](gui::Canvas& canvas, gfx::Bitmap& bitmap, int width,
1535 int height, int tile_size, bool is_loaded) {
1536 canvas.DrawBackground(ImVec2(width + 1, height + 1));
1537 canvas.DrawContextMenu();
1538 canvas.DrawBitmap(bitmap, 2, is_loaded);
1539 canvas.DrawTileSelector(tile_size);
1540 canvas.DrawGrid(tile_size);
1541 canvas.DrawOverlay();
1542 };
1543
1544 if (scrollbar) {
1545 if (ImGuiID child_id =
1546 ImGui::GetID((ImTextureID)(intptr_t)(intptr_t)canvas_id);
1547 ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
1548 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
1549 draw_canvas(canvas, bitmap, width, height, tile_size, is_loaded);
1550 }
1551 ImGui::EndChild();
1552 } else {
1553 draw_canvas(canvas, bitmap, width, height, tile_size, is_loaded);
1554 }
1555}
1556
1558 const std::string& label, bool auto_resize) {
1559 // Configure canvas for table integration
1560 canvas.SetAutoResize(auto_resize);
1561
1562 if (auto_resize && bitmap.is_active()) {
1563 // Auto-calculate size based on bitmap content
1564 ImVec2 content_size = ImVec2(bitmap.width(), bitmap.height());
1565 ImVec2 preferred_size = CanvasUtils::CalculatePreferredCanvasSize(
1566 content_size, canvas.GetGlobalScale());
1567 canvas.SetCanvasSize(preferred_size);
1568 }
1569
1570 // Begin table-aware canvas
1571 if (canvas.BeginTableCanvas(label)) {
1572 // Draw the canvas content
1573 canvas.DrawBackground();
1574 canvas.DrawContextMenu();
1575
1576 if (bitmap.is_active()) {
1577 canvas.DrawBitmap(bitmap, 2, 2, canvas.GetGlobalScale());
1578 }
1579
1580 canvas.DrawGrid();
1581 canvas.DrawOverlay();
1582 }
1583 canvas.EndTableCanvas();
1584}
1585
1587 // Use the new modal system if available
1588 if (modals_) {
1589 canvas::CanvasConfig modal_config;
1590 modal_config.canvas_size = canvas_sz_;
1591 modal_config.content_size = config_.content_size;
1592 modal_config.global_scale = global_scale_;
1593 modal_config.grid_step = custom_step_;
1594 modal_config.enable_grid = enable_grid_;
1598 modal_config.is_draggable = draggable_;
1599 modal_config.auto_resize = config_.auto_resize;
1600 modal_config.scrolling = scrolling_;
1601 modal_config.on_config_changed =
1602 [this](const canvas::CanvasConfig& updated_config) {
1603 // Update legacy variables when config changes
1604 enable_grid_ = updated_config.enable_grid;
1605 enable_hex_tile_labels_ = updated_config.enable_hex_labels;
1606 enable_custom_labels_ = updated_config.enable_custom_labels;
1607 };
1608 modal_config.on_scale_changed =
1609 [this](const canvas::CanvasConfig& updated_config) {
1610 global_scale_ = updated_config.global_scale;
1611 scrolling_ = updated_config.scrolling;
1612 };
1613
1614 modals_->ShowAdvancedProperties(canvas_id_, modal_config, bitmap_);
1615 return;
1616 }
1617
1618 // Fallback to legacy modal system
1619 if (ImGui::BeginPopupModal("Advanced Canvas Properties", nullptr,
1620 ImGuiWindowFlags_AlwaysAutoResize)) {
1621 ImGui::Text("Advanced Canvas Configuration");
1622 ImGui::Separator();
1623
1624 // Canvas properties (read-only info)
1625 ImGui::Text("Canvas Properties");
1626 ImGui::Text("ID: %s", canvas_id_.c_str());
1627 ImGui::Text("Canvas Size: %.0f x %.0f", config_.canvas_size.x,
1629 ImGui::Text("Content Size: %.0f x %.0f", config_.content_size.x,
1631 ImGui::Text("Global Scale: %.3f", config_.global_scale);
1632 ImGui::Text("Grid Step: %.1f", config_.grid_step);
1633
1634 if (config_.content_size.x > 0 && config_.content_size.y > 0) {
1635 ImVec2 min_size = GetMinimumSize();
1636 ImVec2 preferred_size = GetPreferredSize();
1637 ImGui::Text("Minimum Size: %.0f x %.0f", min_size.x, min_size.y);
1638 ImGui::Text("Preferred Size: %.0f x %.0f", preferred_size.x,
1639 preferred_size.y);
1640 }
1641
1642 // Editable properties using new config system
1643 ImGui::Separator();
1644 ImGui::Text("View Settings");
1645 if (ImGui::Checkbox("Enable Grid", &config_.enable_grid)) {
1646 enable_grid_ = config_.enable_grid; // Legacy sync
1647 }
1648 if (ImGui::Checkbox("Enable Hex Labels", &config_.enable_hex_labels)) {
1650 }
1651 if (ImGui::Checkbox("Enable Custom Labels",
1654 }
1655 if (ImGui::Checkbox("Enable Context Menu", &config_.enable_context_menu)) {
1657 }
1658 if (ImGui::Checkbox("Draggable", &config_.is_draggable)) {
1659 draggable_ = config_.is_draggable; // Legacy sync
1660 }
1661 if (ImGui::Checkbox("Auto Resize for Tables", &config_.auto_resize)) {
1662 // Auto resize setting changed
1663 }
1664
1665 // Grid controls
1666 ImGui::Separator();
1667 ImGui::Text("Grid Configuration");
1668 if (ImGui::SliderFloat("Grid Step", &config_.grid_step, 1.0f, 128.0f,
1669 "%.1f")) {
1670 custom_step_ = config_.grid_step; // Legacy sync
1671 }
1672
1673 // Scale controls
1674 ImGui::Separator();
1675 ImGui::Text("Scale Configuration");
1676 if (ImGui::SliderFloat("Global Scale", &config_.global_scale, 0.1f, 10.0f,
1677 "%.2f")) {
1678 global_scale_ = config_.global_scale; // Legacy sync
1679 }
1680
1681 // Scrolling controls
1682 ImGui::Separator();
1683 ImGui::Text("Scrolling Configuration");
1684 ImGui::Text("Current Scroll: %.1f, %.1f", scrolling_.x, scrolling_.y);
1685 if (ImGui::Button("Reset Scroll")) {
1686 scrolling_ = ImVec2(0, 0);
1687 }
1688 ImGui::SameLine();
1689 if (ImGui::Button("Center View")) {
1690 if (bitmap_) {
1691 scrolling_ = ImVec2(
1693 2.0f,
1695 config_.canvas_size.y) /
1696 2.0f);
1697 }
1698 }
1699
1700 if (ImGui::Button("Close")) {
1701 ImGui::CloseCurrentPopup();
1702 }
1703 ImGui::EndPopup();
1704 }
1705}
1706
1707// Old ShowPaletteManager method removed - now handled by PaletteWidget
1708
1710 // Use the new modal system if available
1711 if (modals_) {
1712 canvas::CanvasConfig modal_config;
1713 modal_config.canvas_size = canvas_sz_;
1714 modal_config.content_size = config_.content_size;
1715 modal_config.global_scale = global_scale_;
1716 modal_config.grid_step = custom_step_;
1717 modal_config.enable_grid = enable_grid_;
1721 modal_config.is_draggable = draggable_;
1722 modal_config.auto_resize = config_.auto_resize;
1723 modal_config.scrolling = scrolling_;
1724 modal_config.on_config_changed =
1725 [this](const canvas::CanvasConfig& updated_config) {
1726 // Update legacy variables when config changes
1727 enable_grid_ = updated_config.enable_grid;
1728 enable_hex_tile_labels_ = updated_config.enable_hex_labels;
1729 enable_custom_labels_ = updated_config.enable_custom_labels;
1730 enable_context_menu_ = updated_config.enable_context_menu;
1731 };
1732 modal_config.on_scale_changed =
1733 [this](const canvas::CanvasConfig& updated_config) {
1734 draggable_ = updated_config.is_draggable;
1735 custom_step_ = updated_config.grid_step;
1736 global_scale_ = updated_config.global_scale;
1737 scrolling_ = updated_config.scrolling;
1738 };
1739
1740 modals_->ShowScalingControls(canvas_id_, modal_config);
1741 return;
1742 }
1743
1744 // Fallback to legacy modal system
1745 if (ImGui::BeginPopupModal("Scaling Controls", nullptr,
1746 ImGuiWindowFlags_AlwaysAutoResize)) {
1747 ImGui::Text("Canvas Scaling and Display Controls");
1748 ImGui::Separator();
1749
1750 // Global scale with new config system
1751 ImGui::Text("Global Scale: %.3f", config_.global_scale);
1752 if (ImGui::SliderFloat("##GlobalScale", &config_.global_scale, 0.1f, 10.0f,
1753 "%.2f")) {
1754 global_scale_ = config_.global_scale; // Legacy sync
1755 }
1756
1757 // Preset scale buttons
1758 ImGui::Text("Preset Scales:");
1759 if (ImGui::Button("0.25x")) {
1760 config_.global_scale = 0.25f;
1762 }
1763 ImGui::SameLine();
1764 if (ImGui::Button("0.5x")) {
1765 config_.global_scale = 0.5f;
1767 }
1768 ImGui::SameLine();
1769 if (ImGui::Button("1x")) {
1770 config_.global_scale = 1.0f;
1772 }
1773 ImGui::SameLine();
1774 if (ImGui::Button("2x")) {
1775 config_.global_scale = 2.0f;
1777 }
1778 ImGui::SameLine();
1779 if (ImGui::Button("4x")) {
1780 config_.global_scale = 4.0f;
1782 }
1783 ImGui::SameLine();
1784 if (ImGui::Button("8x")) {
1785 config_.global_scale = 8.0f;
1787 }
1788
1789 // Grid configuration
1790 ImGui::Separator();
1791 ImGui::Text("Grid Configuration");
1792 ImGui::Text("Grid Step: %.1f", config_.grid_step);
1793 if (ImGui::SliderFloat("##GridStep", &config_.grid_step, 1.0f, 128.0f,
1794 "%.1f")) {
1795 custom_step_ = config_.grid_step; // Legacy sync
1796 }
1797
1798 // Grid size presets
1799 ImGui::Text("Grid Presets:");
1800 if (ImGui::Button("8x8")) {
1801 config_.grid_step = 8.0f;
1803 }
1804 ImGui::SameLine();
1805 if (ImGui::Button("16x16")) {
1806 config_.grid_step = 16.0f;
1808 }
1809 ImGui::SameLine();
1810 if (ImGui::Button("32x32")) {
1811 config_.grid_step = 32.0f;
1813 }
1814 ImGui::SameLine();
1815 if (ImGui::Button("64x64")) {
1816 config_.grid_step = 64.0f;
1818 }
1819
1820 // Canvas size info
1821 ImGui::Separator();
1822 ImGui::Text("Canvas Information");
1823 ImGui::Text("Canvas Size: %.0f x %.0f", config_.canvas_size.x,
1825 ImGui::Text("Scaled Size: %.0f x %.0f",
1828 if (bitmap_) {
1829 ImGui::Text("Bitmap Size: %d x %d", bitmap_->width(), bitmap_->height());
1830 ImGui::Text(
1831 "Effective Scale: %.3f x %.3f",
1834 }
1835
1836 if (ImGui::Button("Close")) {
1837 ImGui::CloseCurrentPopup();
1838 }
1839 ImGui::EndPopup();
1840 }
1841}
1842
1843// BPP format management methods
1845 if (!bpp_format_ui_) {
1847 std::make_unique<gui::BppFormatUI>(canvas_id_ + "_bpp_format");
1848 }
1849
1850 if (bitmap_) {
1851 bpp_format_ui_->RenderFormatSelector(
1853 [this](gfx::BppFormat format) { ConvertBitmapFormat(format); });
1854 }
1855}
1856
1858 if (!bpp_format_ui_) {
1860 std::make_unique<gui::BppFormatUI>(canvas_id_ + "_bpp_format");
1861 }
1862
1863 if (bitmap_) {
1864 bpp_format_ui_->RenderAnalysisPanel(*bitmap_, bitmap_->palette());
1865 }
1866}
1867
1870 bpp_conversion_dialog_ = std::make_unique<gui::BppConversionDialog>(
1871 canvas_id_ + "_bpp_conversion");
1872 }
1873
1874 if (bitmap_) {
1876 *bitmap_, bitmap_->palette(),
1877 [this](gfx::BppFormat format, bool /*preserve_palette*/) {
1878 ConvertBitmapFormat(format);
1879 });
1880 }
1881
1882 bpp_conversion_dialog_->Render();
1883}
1884
1886 if (!bitmap_)
1887 return false;
1888
1889 gfx::BppFormat current_format = GetCurrentBppFormat();
1890 if (current_format == target_format) {
1891 return true; // No conversion needed
1892 }
1893
1894 try {
1895 // Convert the bitmap data
1896 auto converted_data = gfx::BppFormatManager::Get().ConvertFormat(
1897 bitmap_->vector(), current_format, target_format, bitmap_->width(),
1898 bitmap_->height());
1899
1900 // Update the bitmap with converted data
1901 bitmap_->set_data(converted_data);
1902
1903 // Update the renderer
1905
1906 return true;
1907 } catch (const std::exception& e) {
1908 SDL_Log("Failed to convert bitmap format: %s", e.what());
1909 return false;
1910 }
1911}
1912
1920
1921// Phase 4A: Canvas Automation API
1923 if (!automation_api_) {
1924 automation_api_ = std::make_unique<CanvasAutomationAPI>(this);
1925 }
1926 return automation_api_.get();
1927}
1928
1929} // namespace yaze::gui
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:71
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:66
const SnesPalette & palette() const
Definition bitmap.h:251
TextureHandle texture() const
Definition bitmap.h:260
const std::vector< uint8_t > & vector() const
Definition bitmap.h:261
bool is_active() const
Definition bitmap.h:264
SnesPalette * mutable_palette()
Definition bitmap.h:252
void WriteColor(int position, const ImVec4 &color)
Write a color to a pixel at the given position.
Definition bitmap.cc:424
int height() const
Definition bitmap.h:254
void set_data(const std::vector< uint8_t > &data)
Definition bitmap.cc:645
int width() const
Definition bitmap.h:253
void UpdateTexture()
Updates the underlying SDL_Texture when it already exists.
Definition bitmap.cc:246
BppFormat DetectFormat(const std::vector< uint8_t > &data, int width, int height)
Detect BPP format from bitmap data.
std::vector< uint8_t > ConvertFormat(const std::vector< uint8_t > &data, BppFormat from_format, BppFormat to_format, int width, int height)
Convert bitmap data between BPP formats.
static BppFormatManager & Get()
Defines an abstract interface for all rendering operations.
Definition irenderer.h:35
RAII timer for automatic timing management.
Programmatic interface for controlling canvas operations.
Modern, robust canvas for drawing and manipulating graphics.
Definition canvas.h:54
ImVec2 scrolling_
Definition canvas.h:448
ImVector< ImVec2 > points_
Definition canvas.h:457
int highlight_tile_id
Definition canvas.h:437
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
Definition canvas.cc:1062
Rom * rom() const
Definition canvas.h:405
std::string canvas_id_
Definition canvas.h:461
void ShowScalingControls()
Definition canvas.cc:1709
bool WasDoubleClicked(ImGuiMouseButton button=ImGuiMouseButton_Left) const
Definition canvas.cc:324
CanvasConfig config_
Definition canvas.h:412
ImVec2 selected_tile_pos_
Definition canvas.h:467
auto global_scale() const
Definition canvas.h:345
ImVec2 canvas_p1_
Definition canvas.h:451
void ShowBppAnalysis()
Definition canvas.cc:1857
void DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color)
Definition canvas.cc:1147
void DrawBitmapGroup(std::vector< int > &group, gfx::Tilemap &tilemap, int tile_size, float scale=1.0f, int local_map_size=0x200, ImVec2 total_map_size=ImVec2(0x1000, 0x1000))
Draw group of bitmaps for multi-tile selection preview.
Definition canvas.cc:1157
bool BeginTableCanvas(const std::string &label="")
Definition canvas.cc:289
void InitializeEnhancedComponents()
Definition canvas.cc:164
void ShowBppConversionDialog()
Definition canvas.cc:1868
CanvasAutomationAPI * GetAutomationAPI()
Definition canvas.cc:1922
std::vector< ContextMenuItem > context_menu_items_
Definition canvas.h:424
void ShowAdvancedCanvasProperties()
Definition canvas.cc:1586
void UpdateInfoGrid(ImVec2 bg_size, float grid_size=64.0f, int label_id=0)
Definition canvas.cc:373
std::unique_ptr< gui::BppFormatUI > bpp_format_ui_
Definition canvas.h:185
void DrawContextMenu()
Definition canvas.cc:441
canvas::CanvasUsage GetUsageMode() const
Definition canvas.cc:199
ImVec2 mouse_pos_in_canvas_
Definition canvas.h:453
bool DrawTilemapPainter(gfx::Tilemap &tilemap, int current_tile)
Definition canvas.cc:774
bool DrawSolidTilePainter(const ImVec4 &color, int size)
Definition canvas.cc:845
bool enable_context_menu_
Definition canvas.h:474
auto draw_list() const
Definition canvas.h:309
void DrawLayeredElements()
Definition canvas.cc:1432
void ReserveTableSpace(const std::string &label="")
Definition canvas.cc:284
bool enable_custom_labels_
Definition canvas.h:473
void ShowUsageReport()
Definition canvas.cc:223
ImVec2 GetMinimumSize() const
Definition canvas.cc:274
bool DrawTileSelector(int size, int size_y=0)
Definition canvas.cc:920
bool ConvertBitmapFormat(gfx::BppFormat target_format)
Definition canvas.cc:1885
void DrawGridLines(float grid_step)
Definition canvas.cc:1339
void DrawContextMenuItem(const ContextMenuItem &item)
Definition canvas.cc:585
void ShowPerformanceUI()
Definition canvas.cc:217
bool custom_canvas_size_
Definition canvas.h:475
void ClearContextMenuItems()
Definition canvas.cc:616
void ApplyConfigSnapshot(const canvas::CanvasConfig &snapshot)
Definition canvas.cc:698
void DrawRect(int x, int y, int w, int h, ImVec4 color)
Definition canvas.cc:1329
bool HasValidSelection() const
Definition canvas.cc:316
bool DrawTilePainter(const Bitmap &bitmap, int size, float scale=1.0f)
Definition canvas.cc:728
ImDrawList * draw_list_
Definition canvas.h:445
ImVector< ImVec2 > selected_points_
Definition canvas.h:466
ImVec2 GetCurrentSize() const
Definition canvas.h:254
void SetCanvasSize(ImVec2 canvas_size)
Definition canvas.h:325
void UpdateColorPainter(gfx::IRenderer *renderer, gfx::Bitmap &bitmap, const ImVec4 &color, const std::function< void()> &event, int tile_size, float scale=1.0f)
Definition canvas.cc:357
void DrawTileOnBitmap(int tile_size, gfx::Bitmap *bitmap, ImVec4 color)
Definition canvas.cc:899
void DrawCustomHighlight(float grid_step)
Definition canvas.cc:1381
bool select_rect_active_
Definition canvas.h:468
Bitmap * bitmap_
Definition canvas.h:443
CanvasGridSize grid_size() const
Definition canvas.h:97
std::vector< PopupState > active_popups_
Definition canvas.h:433
ImVec2 GetPreferredSize() const
Definition canvas.cc:279
void InitializePaletteEditor(Rom *rom)
Definition canvas.cc:245
void Begin(ImVec2 canvas_size=ImVec2(0, 0))
Begin canvas rendering (ImGui-style)
Definition canvas.cc:338
auto canvas_size() const
Definition canvas.h:314
std::shared_ptr< canvas::CanvasUsageTracker > usage_tracker_
Definition canvas.h:192
void SetZoomToFit(const gfx::Bitmap &bitmap)
Definition canvas.cc:673
bool WasClicked(ImGuiMouseButton button=ImGuiMouseButton_Left) const
Definition canvas.cc:320
ImVector< ImVector< std::string > > labels_
Definition canvas.h:458
gfx::BppFormat GetCurrentBppFormat() const
Definition canvas.cc:1913
auto canvas_id() const
Definition canvas.h:352
void ClosePersistentPopup(const std::string &popup_id)
Definition canvas.cc:640
void ShowBppFormatSelector()
Definition canvas.cc:1844
void RecordCanvasOperation(const std::string &operation_name, double time_ms)
Definition canvas.cc:206
void RenderPersistentPopups()
Definition canvas.cc:650
void SetGridSize(CanvasGridSize grid_size)
Definition canvas.h:77
bool IsAutoResize() const
Definition canvas.h:256
void SetUsageMode(canvas::CanvasUsage usage)
Definition canvas.cc:190
void End()
End canvas rendering (ImGui-style)
Definition canvas.cc:344
float GetGlobalScale() const
Definition canvas.h:329
std::unique_ptr< gui::BppConversionDialog > bpp_conversion_dialog_
Definition canvas.h:186
void DrawSelectRect(int current_map, int tile_size=0x10, float scale=1.0f)
Definition canvas.cc:948
auto zero_point() const
Definition canvas.h:310
std::unique_ptr< PaletteWidget > palette_editor_
Definition canvas.h:414
std::unique_ptr< canvas::CanvasContextMenu > context_menu_
Definition canvas.h:191
ImVec2 GetLastClickPosition() const
Definition canvas.cc:329
void ShowPaletteEditor()
Definition canvas.cc:252
std::unique_ptr< canvas::CanvasModals > modals_
Definition canvas.h:190
float global_scale_
Definition canvas.h:470
void DrawOutline(int x, int y, int w, int h)
Definition canvas.cc:1142
float custom_step_
Definition canvas.h:469
void DrawInfoGrid(float grid_step=64.0f, int tile_id_offset=8, int label_id=0)
Definition canvas.cc:1344
CanvasSelection selection_
Definition canvas.h:413
bool enable_hex_tile_labels_
Definition canvas.h:472
ImVec2 canvas_p0_
Definition canvas.h:450
void OpenPersistentPopup(const std::string &popup_id, std::function< void()> render_callback)
Definition canvas.cc:620
void DrawBitmapTable(const BitmapTable &gfx_bin)
Definition canvas.cc:1129
std::string context_id_
Definition canvas.h:462
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0))
Definition canvas.cc:381
ImVec2 canvas_sz_
Definition canvas.h:449
void InitializeDefaults()
Definition canvas.cc:110
void EndTableCanvas()
Definition canvas.cc:311
std::shared_ptr< canvas::CanvasPerformanceIntegration > performance_integration_
Definition canvas.h:193
std::unique_ptr< CanvasAutomationAPI > automation_api_
Definition canvas.h:417
void SetAutoResize(bool auto_resize)
Definition canvas.h:255
void SetGlobalScale(float scale)
Definition canvas.h:330
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
Definition canvas.cc:1386
void DrawText(const std::string &text, int x, int y)
Definition canvas.cc:1334
ImVec2 drawn_tile_pos_
Definition canvas.h:452
canvas::CanvasInteractionHandler interaction_handler_
Definition canvas.h:194
void AddContextMenuItem(const ContextMenuItem &item)
Definition canvas.cc:612
std::vector< ImVec2 > selected_tiles_
Definition canvas.h:465
bool ApplyROMPalette(int group_index, int palette_index)
Definition canvas.cc:266
void ShowColorAnalysis()
Definition canvas.cc:260
void ApplyScaleSnapshot(const canvas::CanvasConfig &snapshot)
Definition canvas.cc:722
void Initialize(const std::string &canvas_id)
Initialize the interaction handler.
void RegisterIntegration(const std::string &canvas_id, std::shared_ptr< CanvasPerformanceIntegration > integration)
Register a canvas performance integration.
void RegisterTracker(const std::string &canvas_id, std::shared_ptr< CanvasUsageTracker > tracker)
Register a canvas tracker.
BppFormat
BPP format enumeration for SNES graphics.
@ kBpp8
8 bits per pixel (256 colors)
std::unordered_map< int, gfx::Bitmap > BitmapTable
Definition bitmap.h:333
void ReserveCanvasSpace(ImVec2 canvas_size, const std::string &label)
void SetNextCanvasSize(ImVec2 size, bool auto_resize)
void DrawCanvasRect(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int x, int y, int w, int h, ImVec4 color, float global_scale)
void DrawCanvasLabels(const CanvasRenderContext &ctx, const ImVector< ImVector< std::string > > &labels, int current_labels, int tile_id_offset)
void DrawCanvasOverlay(const CanvasRenderContext &ctx, const ImVector< ImVec2 > &points, const ImVector< ImVec2 > &selected_points)
ImVec2 CalculateCanvasSize(ImVec2 content_region, ImVec2 custom_size, bool use_custom)
ImVec2 CalculateMinimumCanvasSize(ImVec2 content_size, float global_scale, float padding)
void DrawCanvasOutline(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int x, int y, int w, int h, uint32_t color)
void DrawCanvasOutlineWithColor(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int x, int y, int w, int h, ImVec4 color)
void DrawCanvasGrid(const CanvasRenderContext &ctx, int highlight_tile_id)
ImVec2 CalculatePreferredCanvasSize(ImVec2 content_size, float global_scale, float min_scale)
void DrawCanvasText(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, const std::string &text, int x, int y, float global_scale)
ImVec2 CalculateScaledCanvasSize(ImVec2 canvas_size, float global_scale)
void DrawCustomHighlight(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int highlight_tile_id, float grid_step)
void DrawCanvasGridLines(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 canvas_p1, ImVec2 scrolling, float grid_step, float global_scale)
ImVec2 AlignPosToGrid(ImVec2 pos, float scale)
Definition canvas.cc:102
CanvasUsage
Canvas usage patterns and tracking.
Graphical User Interface (GUI) components for the application.
constexpr uint32_t kWhiteColor
Definition canvas.cc:96
constexpr uint32_t kRectangleColor
Definition canvas.cc:95
void BitmapCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap, int width, int height, int tile_size, bool is_loaded, bool scrollbar, int canvas_id)
Definition canvas.cc:1531
void EndCanvas(Canvas &canvas)
Definition canvas.cc:1494
void BeginPadding(int i)
Definition style.cc:272
void BeginCanvas(Canvas &canvas, ImVec2 child_size)
Definition canvas.cc:1473
void GraphicsBinCanvasPipeline(int width, int height, int tile_size, int num_sheets_to_load, int canvas_id, bool is_loaded, gfx::BitmapTable &graphics_bin)
Definition canvas.cc:1500
void EndPadding()
Definition style.cc:276
CanvasGridSize
Definition canvas.h:41
void TableCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap, const std::string &label, bool auto_resize)
Definition canvas.cc:1557
constexpr ImGuiButtonFlags kMouseFlags
Definition canvas.cc:98
int y
Y coordinate or height.
Definition tilemap.h:20
int x
X coordinate or width.
Definition tilemap.h:19
Tilemap structure for SNES tile-based graphics management.
Definition tilemap.h:109
Pair tile_size
Size of individual tiles (8x8 or 16x16)
Definition tilemap.h:113
Pair map_size
Size of tilemap in tiles.
Definition tilemap.h:114
Bitmap atlas
Master bitmap containing all tiles.
Definition tilemap.h:110
std::function< void()> callback
Definition canvas.h:155
std::vector< ContextMenuItem > subitems
Definition canvas.h:157
std::function< bool()> enabled_condition
Definition canvas.h:156
std::function< void()> render_callback
Definition canvas.h:431
Canvas configuration options for modals.
std::function< void(const CanvasConfig &)> on_config_changed
std::function< void(const CanvasConfig &)> on_scale_changed