yaze 0.2.0
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>
5
8#include "app/gfx/bitmap.h"
9#include "app/gui/color.h"
10#include "app/gui/input.h"
11#include "app/gui/style.h"
12#include "app/rom.h"
13#include "imgui/imgui.h"
14
15namespace yaze {
16namespace app {
17namespace gui {
18
19using core::Renderer;
20
21using ImGui::BeginMenu;
22using ImGui::BeginPopup;
23using ImGui::EndMenu;
24using ImGui::GetContentRegionAvail;
25using ImGui::GetCursorScreenPos;
26using ImGui::GetIO;
27using ImGui::GetMouseDragDelta;
28using ImGui::GetWindowDrawList;
29using ImGui::IsItemActive;
30using ImGui::IsItemHovered;
31using ImGui::IsMouseClicked;
32using ImGui::IsMouseDragging;
33using ImGui::MenuItem;
34using ImGui::OpenPopupOnItemClick;
35using ImGui::Selectable;
36using ImGui::Separator;
37using ImGui::Text;
38
39constexpr uint32_t kBlackColor = IM_COL32(0, 0, 0, 255);
40constexpr uint32_t kRectangleColor = IM_COL32(32, 32, 32, 255);
41constexpr uint32_t kWhiteColor = IM_COL32(255, 255, 255, 255);
42constexpr uint32_t kOutlineRect = IM_COL32(255, 255, 255, 200);
43
44constexpr ImGuiButtonFlags kMouseFlags =
45 ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight;
46
47void Canvas::UpdateColorPainter(gfx::Bitmap &bitmap, const ImVec4 &color,
48 const std::function<void()> &event,
49 int tile_size, float scale) {
50 global_scale_ = scale;
53 DrawBitmap(bitmap, 2, scale);
54 if (DrawSolidTilePainter(color, tile_size)) {
55 event();
56 }
57 DrawGrid();
59}
60
61void Canvas::UpdateInfoGrid(ImVec2 bg_size, int tile_size, float scale,
62 float grid_size, int label_id) {
64 DrawBackground(bg_size);
65 DrawInfoGrid(grid_size, 8, label_id);
67}
68
69void Canvas::DrawBackground(ImVec2 canvas_size, bool can_drag) {
70 draw_list_ = GetWindowDrawList();
71 canvas_p0_ = GetCursorScreenPos();
72 if (!custom_canvas_size_) canvas_sz_ = GetContentRegionAvail();
76
77 // Draw border and background color
80
81 ImGui::InvisibleButton(
82 canvas_id_.c_str(),
85
86 if (draggable_ && IsItemHovered()) {
87 const ImGuiIO &io = GetIO();
88 const bool is_active = IsItemActive(); // Held
89 const ImVec2 origin(canvas_p0_.x + scrolling_.x,
90 canvas_p0_.y + scrolling_.y); // Lock scrolled origin
91 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
92
93 // Pan (we use a zero mouse threshold when there's no context menu)
94 if (const float mouse_threshold_for_pan =
95 enable_context_menu_ ? -1.0f : 0.0f;
96 is_active &&
97 IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan)) {
98 scrolling_.x += io.MouseDelta.x;
99 scrolling_.y += io.MouseDelta.y;
100 }
101 }
102}
103
105 const ImGuiIO &io = GetIO();
106 const ImVec2 scaled_sz(canvas_sz_.x * global_scale_,
108 const ImVec2 origin(canvas_p0_.x + scrolling_.x,
109 canvas_p0_.y + scrolling_.y); // Lock scrolled origin
110 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
111
112 // Context menu (under default mouse threshold)
113 if (ImVec2 drag_delta = GetMouseDragDelta(ImGuiMouseButton_Right);
114 enable_context_menu_ && drag_delta.x == 0.0f && drag_delta.y == 0.0f)
115 OpenPopupOnItemClick(context_id_.c_str(), ImGuiPopupFlags_MouseButtonRight);
116
117 // Contents of the Context Menu
118 if (ImGui::BeginPopup(context_id_.c_str())) {
119 if (MenuItem("Reset Position", nullptr, false)) {
120 scrolling_.x = 0;
121 scrolling_.y = 0;
122 }
123 MenuItem("Show Grid", nullptr, &enable_grid_);
124 Selectable("Show Position Labels", &enable_hex_tile_labels_);
125 if (BeginMenu("Canvas Properties")) {
126 Text("Canvas Size: %.0f x %.0f", canvas_sz_.x, canvas_sz_.y);
127 Text("Global Scale: %.1f", global_scale_);
128 Text("Mouse Position: %.0f x %.0f", mouse_pos.x, mouse_pos.y);
129 EndMenu();
130 }
131 if (bitmap != nullptr) {
132 if (BeginMenu("Bitmap Properties")) {
133 Text("Size: %.0f x %.0f", scaled_sz.x, scaled_sz.y);
134 Text("Pitch: %d", bitmap->surface()->pitch);
135 Text("BitsPerPixel: %d", bitmap->surface()->format->BitsPerPixel);
136 Text("BytesPerPixel: %d", bitmap->surface()->format->BytesPerPixel);
137 EndMenu();
138 }
139 if (BeginMenu("Bitmap Format")) {
140 if (MenuItem("Indexed")) {
143 }
144 if (MenuItem("2BPP")) {
147 }
148 if (MenuItem("4BPP")) {
151 }
152 if (MenuItem("8BPP")) {
155 }
156 EndMenu();
157 }
158 if (BeginMenu("Bitmap Palette")) {
159 if (rom()->is_loaded()) {
160 gui::TextWithSeparators("ROM Palette");
161 ImGui::SetNextItemWidth(100.f);
162 ImGui::Combo("Palette Group", (int *)&edit_palette_group_name_index_,
164 IM_ARRAYSIZE(gfx::kPaletteGroupAddressesKeys));
165 ImGui::SetNextItemWidth(100.f);
166 gui::InputHexWord("Palette Group Index", &edit_palette_index_);
167
168 auto palette_group = rom()->mutable_palette_group()->get_group(
170 auto palette = palette_group->mutable_palette(edit_palette_index_);
171
172 if (ImGui::BeginChild("Palette", ImVec2(0, 300), true)) {
174 refresh_graphics_, *palette);
175
176 if (refresh_graphics_) {
177 auto status = bitmap->ApplyPaletteWithTransparent(
178 *palette, edit_palette_sub_index_);
180 refresh_graphics_ = false;
181 }
182 ImGui::EndChild();
183 }
184 }
185 EndMenu();
186 }
187 }
188 ImGui::Separator();
189 if (BeginMenu("Grid Tile Size")) {
190 if (MenuItem("8x8", nullptr, custom_step_ == 8.0f)) {
191 custom_step_ = 8.0f;
192 }
193 if (MenuItem("16x16", nullptr, custom_step_ == 16.0f)) {
194 custom_step_ = 16.0f;
195 }
196 if (MenuItem("32x32", nullptr, custom_step_ == 32.0f)) {
197 custom_step_ = 32.0f;
198 }
199 if (MenuItem("64x64", nullptr, custom_step_ == 64.0f)) {
200 custom_step_ = 64.0f;
201 }
202 EndMenu();
203 }
204
205 ImGui::EndPopup();
206 }
207}
208
209bool Canvas::DrawTilePainter(const Bitmap &bitmap, int size, float scale) {
210 const ImGuiIO &io = GetIO();
211 const bool is_hovered = IsItemHovered();
212 is_hovered_ = is_hovered;
213 // Lock scrolled origin
214 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
215 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
216
217 if (is_hovered) {
218 // Reset the previous tile hover
219 if (!points_.empty()) {
220 points_.clear();
221 }
222
223 // Calculate the coordinates of the mouse
224 ImVec2 painter_pos;
225 painter_pos.x =
226 std::floor((double)mouse_pos.x / (size * scale)) * (size * scale);
227 painter_pos.y =
228 std::floor((double)mouse_pos.y / (size * scale)) * (size * scale);
229
230 mouse_pos_in_canvas_ = painter_pos;
231
232 auto painter_pos_end =
233 ImVec2(painter_pos.x + (size * scale), painter_pos.y + (size * scale));
234 points_.push_back(painter_pos);
235 points_.push_back(painter_pos_end);
236
237 if (bitmap.is_active()) {
238 draw_list_->AddImage(
239 (void *)bitmap.texture(),
240 ImVec2(origin.x + painter_pos.x, origin.y + painter_pos.y),
241 ImVec2(origin.x + painter_pos.x + (size)*scale,
242 origin.y + painter_pos.y + size * scale));
243 }
244
245 if (IsMouseClicked(ImGuiMouseButton_Left)) {
246 // Draw the currently selected tile on the overworld here
247 // Save the coordinates of the selected tile.
248 drawn_tile_pos_ = painter_pos;
249 return true;
250 } else if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
251 // Draw the currently selected tile on the overworld here
252 // Save the coordinates of the selected tile.
253 drawn_tile_pos_ = painter_pos;
254 return true;
255 }
256 } else {
257 // Erase the hover when the mouse is not in the canvas window.
258 points_.clear();
259 }
260 return false;
261}
262
263bool Canvas::DrawSolidTilePainter(const ImVec4 &color, int tile_size) {
264 const ImGuiIO &io = GetIO();
265 const bool is_hovered = IsItemHovered();
266 is_hovered_ = is_hovered;
267 // Lock scrolled origin
268 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
269 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
270 auto scaled_tile_size = tile_size * global_scale_;
271
272 static bool is_dragging = false;
273 static ImVec2 start_drag_pos;
274
275 if (is_hovered) {
276 // Reset the previous tile hover
277 if (!points_.empty()) {
278 points_.clear();
279 }
280
281 // Calculate the coordinates of the mouse
282 ImVec2 painter_pos;
283 painter_pos.x =
284 std::floor((double)mouse_pos.x / scaled_tile_size) * scaled_tile_size;
285 painter_pos.y =
286 std::floor((double)mouse_pos.y / scaled_tile_size) * scaled_tile_size;
287
288 // Clamp the size to a grid
289 painter_pos.x =
290 std::clamp(painter_pos.x, 0.0f, canvas_sz_.x * global_scale_);
291 painter_pos.y =
292 std::clamp(painter_pos.y, 0.0f, canvas_sz_.y * global_scale_);
293
294 points_.push_back(painter_pos);
295 points_.push_back(ImVec2(painter_pos.x + scaled_tile_size,
296 painter_pos.y + scaled_tile_size));
297
298 draw_list_->AddRectFilled(
299 ImVec2(origin.x + painter_pos.x + 1, origin.y + painter_pos.y + 1),
300 ImVec2(origin.x + painter_pos.x + scaled_tile_size,
301 origin.y + painter_pos.y + scaled_tile_size),
302 IM_COL32(color.x * 255, color.y * 255, color.z * 255, 255));
303
304 if (IsMouseClicked(ImGuiMouseButton_Left)) {
305 is_dragging = true;
306 start_drag_pos = painter_pos;
307 }
308
309 if (is_dragging && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
310 is_dragging = false;
311 drawn_tile_pos_ = start_drag_pos;
312 return true;
313 }
314
315 } else {
316 // Erase the hover when the mouse is not in the canvas window.
317 points_.clear();
318 }
319 return false;
320}
321
322void Canvas::DrawTileOnBitmap(int tile_size, gfx::Bitmap *bitmap,
323 ImVec4 color) {
324 const ImVec2 position = drawn_tile_pos_;
325 int tile_index_x = static_cast<int>(position.x / global_scale_) / tile_size;
326 int tile_index_y = static_cast<int>(position.y / global_scale_) / tile_size;
327
328 ImVec2 start_position(tile_index_x * tile_size, tile_index_y * tile_size);
329
330 // Update the bitmap's pixel data based on the start_position and color
331 for (int y = 0; y < tile_size; ++y) {
332 for (int x = 0; x < tile_size; ++x) {
333 // Calculate the actual pixel index in the bitmap
334 int pixel_index =
335 (start_position.y + y) * bitmap->width() + (start_position.x + x);
336
337 // Write the color to the pixel
338 bitmap->WriteColor(pixel_index, color);
339 }
340 }
341}
342
344 const ImGuiIO &io = GetIO();
345 const bool is_hovered = IsItemHovered();
346 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
347 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
348
349 if (is_hovered && IsMouseClicked(ImGuiMouseButton_Left)) {
350 if (!points_.empty()) {
351 points_.clear();
352 }
353 ImVec2 painter_pos;
354 painter_pos.x = std::floor((double)mouse_pos.x / size) * size;
355 painter_pos.y = std::floor((double)mouse_pos.y / size) * size;
356
357 points_.push_back(painter_pos);
358 points_.push_back(ImVec2(painter_pos.x + size, painter_pos.y + size));
359 mouse_pos_in_canvas_ = painter_pos;
360 }
361
362 if (is_hovered && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
363 return true;
364 }
365
366 return false;
367}
368
369namespace {
370ImVec2 AlignPosToGrid(ImVec2 pos, float scale) {
371 return ImVec2(std::floor((double)pos.x / scale) * scale,
372 std::floor((double)pos.y / scale) * scale);
373}
374} // namespace
375
376void Canvas::DrawSelectRect(int current_map, int tile_size, float scale) {
377 const ImGuiIO &io = GetIO();
378 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
379 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
380 static ImVec2 drag_start_pos;
381 const float scaled_size = tile_size * scale;
382 static bool dragging = false;
383 constexpr int small_map_size = 0x200;
384 int superY = current_map / 8;
385 int superX = current_map % 8;
386
387 // Handle right click for single tile selection
388 if (IsMouseClicked(ImGuiMouseButton_Right)) {
389 ImVec2 painter_pos = AlignPosToGrid(mouse_pos, scaled_size);
390 int painter_x = painter_pos.x;
391 int painter_y = painter_pos.y;
392
393 auto tile16_x = (painter_x % small_map_size) / (small_map_size / 0x20);
394 auto tile16_y = (painter_y % small_map_size) / (small_map_size / 0x20);
395
396 int index_x = superX * 0x20 + tile16_x;
397 int index_y = superY * 0x20 + tile16_y;
398 selected_tile_pos_ = ImVec2(index_x, index_y);
399 selected_points_.clear();
400 select_rect_active_ = false;
401
402 // Start drag position for rectangle selection
403 drag_start_pos = AlignPosToGrid(mouse_pos, scaled_size);
404 }
405
406 // Calculate the rectangle's top-left and bottom-right corners
407 ImVec2 drag_end_pos = AlignPosToGrid(mouse_pos, scaled_size);
408 if (ImGui::IsMouseDragging(ImGuiMouseButton_Right)) {
409 auto start = ImVec2(canvas_p0_.x + drag_start_pos.x,
410 canvas_p0_.y + drag_start_pos.y);
411 auto end = ImVec2(canvas_p0_.x + drag_end_pos.x + tile_size,
412 canvas_p0_.y + drag_end_pos.y + tile_size);
413 draw_list_->AddRect(start, end, kWhiteColor);
414 dragging = true;
415 }
416
417 if (dragging) {
418 // Release dragging mode
419 if (!ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
420 dragging = false;
421
422 // Calculate the bounds of the rectangle in terms of 16x16 tile indices
423 constexpr int tile16_size = 16;
424 int start_x = std::floor(drag_start_pos.x / scaled_size) * tile16_size;
425 int start_y = std::floor(drag_start_pos.y / scaled_size) * tile16_size;
426 int end_x = std::floor(drag_end_pos.x / scaled_size) * tile16_size;
427 int end_y = std::floor(drag_end_pos.y / scaled_size) * tile16_size;
428
429 // Swap the start and end positions if they are in the wrong order
430 if (start_x > end_x) std::swap(start_x, end_x);
431 if (start_y > end_y) std::swap(start_y, end_y);
432
433 selected_tiles_.clear();
434 // Number of tiles per local map (since each tile is 16x16)
435 constexpr int tiles_per_local_map = small_map_size / 16;
436
437 // Loop through the tiles in the rectangle and store their positions
438 for (int y = start_y; y <= end_y; y += tile16_size) {
439 for (int x = start_x; x <= end_x; x += tile16_size) {
440 // Determine which local map (512x512) the tile is in
441 int local_map_x = x / small_map_size;
442 int local_map_y = y / small_map_size;
443
444 // Calculate the tile's position within its local map
445 int tile16_x = (x % small_map_size) / tile16_size;
446 int tile16_y = (y % small_map_size) / tile16_size;
447
448 // Calculate the index within the overall map structure
449 int index_x = local_map_x * tiles_per_local_map + tile16_x;
450 int index_y = local_map_y * tiles_per_local_map + tile16_y;
451
452 selected_tiles_.push_back(ImVec2(index_x, index_y));
453 }
454 }
455 // Clear and add the calculated rectangle points
456 selected_points_.clear();
457 selected_points_.push_back(drag_start_pos);
458 selected_points_.push_back(drag_end_pos);
459 select_rect_active_ = true;
460 }
461 }
462}
463
464void Canvas::DrawBitmap(const Bitmap &bitmap, int border_offset, bool ready) {
465 if (ready) {
466 draw_list_->AddImage(
467 (void *)bitmap.texture(),
468 ImVec2(canvas_p0_.x + border_offset, canvas_p0_.y + border_offset),
469 ImVec2(canvas_p0_.x + (bitmap.width() * 2),
470 canvas_p0_.y + (bitmap.height() * 2)));
471 }
472}
473
474void Canvas::DrawBitmap(const Bitmap &bitmap, int border_offset, float scale) {
475 if (!bitmap.is_active()) {
476 return;
477 }
478 draw_list_->AddImage((void *)bitmap.texture(),
479 ImVec2(canvas_p0_.x, canvas_p0_.y),
480 ImVec2(canvas_p0_.x + (bitmap.width() * scale),
481 canvas_p0_.y + (bitmap.height() * scale)));
483}
484
485void Canvas::DrawBitmap(const Bitmap &bitmap, int x_offset, int y_offset,
486 float scale, int alpha) {
487 if (!bitmap.is_active()) {
488 return;
489 }
490 draw_list_->AddImage(
491 (void *)bitmap.texture(),
492 ImVec2(canvas_p0_.x + x_offset + scrolling_.x,
493 canvas_p0_.y + y_offset + scrolling_.y),
494 ImVec2(
495 canvas_p0_.x + x_offset + scrolling_.x + (bitmap.width() * scale),
496 canvas_p0_.y + y_offset + scrolling_.y + (bitmap.height() * scale)),
497 ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, alpha));
498}
499
500// TODO: Add parameters for sizing and positioning
502 for (const auto &[key, value] : gfx_bin) {
503 int offset = 0x40 * (key + 1);
504 int top_left_y = canvas_p0_.y + 2;
505 if (key >= 1) {
506 top_left_y = canvas_p0_.y + 0x40 * key;
507 }
508 draw_list_->AddImage((void *)value.texture(),
509 ImVec2(canvas_p0_.x + 2, top_left_y),
510 ImVec2(canvas_p0_.x + 0x100, canvas_p0_.y + offset));
511 }
512}
513
514void Canvas::DrawOutline(int x, int y, int w, int h) {
515 ImVec2 origin(canvas_p0_.x + scrolling_.x + x,
516 canvas_p0_.y + scrolling_.y + y);
517 ImVec2 size(canvas_p0_.x + scrolling_.x + x + w,
518 canvas_p0_.y + scrolling_.y + y + h);
519 draw_list_->AddRect(origin, size, kOutlineRect, 0, 0, 1.5f);
520}
521
522void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color) {
523 ImVec2 origin(canvas_p0_.x + scrolling_.x + x,
524 canvas_p0_.y + scrolling_.y + y);
525 ImVec2 size(canvas_p0_.x + scrolling_.x + x + w,
526 canvas_p0_.y + scrolling_.y + y + h);
527 draw_list_->AddRect(origin, size,
528 IM_COL32(color.x, color.y, color.z, color.w));
529}
530
531void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, uint32_t color) {
532 ImVec2 origin(canvas_p0_.x + scrolling_.x + x,
533 canvas_p0_.y + scrolling_.y + y);
534 ImVec2 size(canvas_p0_.x + scrolling_.x + x + w,
535 canvas_p0_.y + scrolling_.y + y + h);
536 draw_list_->AddRect(origin, size, color);
537}
538
539void Canvas::DrawBitmapGroup(std::vector<int> &group,
540 std::vector<gfx::Bitmap> &tile16_individual_,
541 int tile_size, float scale) {
542 if (selected_points_.size() != 2) {
543 // points_ should contain exactly two points
544 return;
545 }
546 if (group.empty()) {
547 // group should not be empty
548 return;
549 }
550
551 // Top-left and bottom-right corners of the rectangle
552 ImVec2 rect_top_left = selected_points_[0];
553 ImVec2 rect_bottom_right = selected_points_[1];
554
555 // Calculate the start and end tiles in the grid
556 int start_tile_x =
557 static_cast<int>(std::floor(rect_top_left.x / (tile_size * scale)));
558 int start_tile_y =
559 static_cast<int>(std::floor(rect_top_left.y / (tile_size * scale)));
560 int end_tile_x =
561 static_cast<int>(std::floor(rect_bottom_right.x / (tile_size * scale)));
562 int end_tile_y =
563 static_cast<int>(std::floor(rect_bottom_right.y / (tile_size * scale)));
564
565 if (start_tile_x > end_tile_x) std::swap(start_tile_x, end_tile_x);
566 if (start_tile_y > end_tile_y) std::swap(start_tile_y, end_tile_y);
567
568 // Calculate the size of the rectangle in 16x16 grid form
569 int rect_width = (end_tile_x - start_tile_x) * tile_size;
570 int rect_height = (end_tile_y - start_tile_y) * tile_size;
571
572 int tiles_per_row = rect_width / tile_size;
573 int tiles_per_col = rect_height / tile_size;
574
575 int i = 0;
576 for (int y = 0; y < tiles_per_col + 1; ++y) {
577 for (int x = 0; x < tiles_per_row + 1; ++x) {
578 int tile_id = group[i];
579
580 // Check if tile_id is within the range of tile16_individual_
581 if (tile_id >= 0 && tile_id < tile16_individual_.size()) {
582 // Calculate the position of the tile within the rectangle
583 int tile_pos_x = (x + start_tile_x) * tile_size * scale;
584 int tile_pos_y = (y + start_tile_y) * tile_size * scale;
585
586 // Draw the tile bitmap at the calculated position
587 DrawBitmap(tile16_individual_[tile_id], tile_pos_x, tile_pos_y, scale,
588 150.0f);
589 i++;
590 }
591 }
592 }
593
594 const ImGuiIO &io = GetIO();
595 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
596 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
597 auto new_start_pos = AlignPosToGrid(mouse_pos, tile_size * scale);
598 selected_points_.clear();
599 selected_points_.push_back(new_start_pos);
600 selected_points_.push_back(
601 ImVec2(new_start_pos.x + rect_width, new_start_pos.y + rect_height));
602 select_rect_active_ = true;
603}
604
605void Canvas::DrawRect(int x, int y, int w, int h, ImVec4 color) {
606 ImVec2 origin(canvas_p0_.x + scrolling_.x + x,
607 canvas_p0_.y + scrolling_.y + y);
608 ImVec2 size(canvas_p0_.x + scrolling_.x + x + w,
609 canvas_p0_.y + scrolling_.y + y + h);
610 draw_list_->AddRectFilled(origin, size,
611 IM_COL32(color.x, color.y, color.z, color.w));
612 // Add a black outline
613 ImVec2 outline_origin(origin.x - 1, origin.y - 1);
614 ImVec2 outline_size(size.x + 1, size.y + 1);
615 draw_list_->AddRect(outline_origin, outline_size, kBlackColor);
616}
617
618void Canvas::DrawText(std::string text, int x, int y) {
619 draw_list_->AddText(ImVec2(canvas_p0_.x + scrolling_.x + x + 1,
620 canvas_p0_.y + scrolling_.y + y + 1),
621 kBlackColor, text.data());
622 draw_list_->AddText(
623 ImVec2(canvas_p0_.x + scrolling_.x + x, canvas_p0_.y + scrolling_.y + y),
624 kWhiteColor, text.data());
625}
626
627void Canvas::DrawGridLines(float grid_step) {
628 const uint32_t grid_color = IM_COL32(200, 200, 200, 50);
629 const float grid_thickness = 0.5f;
630 for (float x = fmodf(scrolling_.x, grid_step);
631 x < canvas_sz_.x * global_scale_; x += grid_step)
632 draw_list_->AddLine(ImVec2(canvas_p0_.x + x, canvas_p0_.y),
633 ImVec2(canvas_p0_.x + x, canvas_p1_.y), grid_color,
634 grid_thickness);
635 for (float y = fmodf(scrolling_.y, grid_step);
636 y < canvas_sz_.y * global_scale_; y += grid_step)
637 draw_list_->AddLine(ImVec2(canvas_p0_.x, canvas_p0_.y + y),
638 ImVec2(canvas_p1_.x, canvas_p0_.y + y), grid_color,
639 grid_thickness);
640}
641
642void Canvas::DrawInfoGrid(float grid_step, int tile_id_offset, int label_id) {
643 // Draw grid + all lines in the canvas
644 draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
645 if (enable_grid_) {
646 if (custom_step_ != 0.f) grid_step = custom_step_;
647 grid_step *= global_scale_; // Apply global scale to grid step
648
649 DrawGridLines(grid_step);
650 DrawCustomHighlight(grid_step);
651
653 // Draw the contents of labels on the grid
654 for (float x = fmodf(scrolling_.x, grid_step);
655 x < canvas_sz_.x * global_scale_; x += grid_step) {
656 for (float y = fmodf(scrolling_.y, grid_step);
657 y < canvas_sz_.y * global_scale_; y += grid_step) {
658 int tile_x = (x - scrolling_.x) / grid_step;
659 int tile_y = (y - scrolling_.y) / grid_step;
660 int tile_id = tile_x + (tile_y * tile_id_offset);
661
662 if (tile_id >= labels_[label_id].size()) {
663 break;
664 }
665 std::string label = labels_[label_id][tile_id];
666 draw_list_->AddText(
667 ImVec2(canvas_p0_.x + x + (grid_step / 2) - tile_id_offset,
668 canvas_p0_.y + y + (grid_step / 2) - tile_id_offset),
669 kWhiteColor, label.data());
670 }
671 }
672 }
673 }
674}
675
676void Canvas::DrawCustomHighlight(float grid_step) {
677 if (highlight_tile_id != -1) {
678 int tile_x = highlight_tile_id % 8;
679 int tile_y = highlight_tile_id / 8;
680 ImVec2 tile_pos(canvas_p0_.x + scrolling_.x + tile_x * grid_step,
681 canvas_p0_.y + scrolling_.y + tile_y * grid_step);
682 ImVec2 tile_pos_end(tile_pos.x + grid_step, tile_pos.y + grid_step);
683
684 draw_list_->AddRectFilled(tile_pos, tile_pos_end,
685 IM_COL32(255, 0, 255, 255));
686 }
687}
688
689void Canvas::DrawGrid(float grid_step, int tile_id_offset) {
690 // Draw grid + all lines in the canvas
691 draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
692 if (enable_grid_) {
693 if (custom_step_ != 0.f) grid_step = custom_step_;
694 grid_step *= global_scale_; // Apply global scale to grid step
695
696 DrawGridLines(grid_step);
697 DrawCustomHighlight(grid_step);
698
700 // Draw the hex ID of the tile in the center of the tile square
701 for (float x = fmodf(scrolling_.x, grid_step);
702 x < canvas_sz_.x * global_scale_; x += grid_step) {
703 for (float y = fmodf(scrolling_.y, grid_step);
704 y < canvas_sz_.y * global_scale_; y += grid_step) {
705 int tile_x = (x - scrolling_.x) / grid_step;
706 int tile_y = (y - scrolling_.y) / grid_step;
707 int tile_id = tile_x + (tile_y * 16);
708 std::string hex_id = absl::StrFormat("%02X", tile_id);
709 draw_list_->AddText(ImVec2(canvas_p0_.x + x + (grid_step / 2) - 4,
710 canvas_p0_.y + y + (grid_step / 2) - 4),
711 kWhiteColor, hex_id.data());
712 }
713 }
714 }
715
717 // Draw the contents of labels on the grid
718 for (float x = fmodf(scrolling_.x, grid_step);
719 x < canvas_sz_.x * global_scale_; x += grid_step) {
720 for (float y = fmodf(scrolling_.y, grid_step);
721 y < canvas_sz_.y * global_scale_; y += grid_step) {
722 int tile_x = (x - scrolling_.x) / grid_step;
723 int tile_y = (y - scrolling_.y) / grid_step;
724 int tile_id = tile_x + (tile_y * tile_id_offset);
725
726 if (tile_id >= labels_[current_labels_].size()) {
727 break;
728 }
729 std::string label = labels_[current_labels_][tile_id];
730 draw_list_->AddText(
731 ImVec2(canvas_p0_.x + x + (grid_step / 2) - tile_id_offset,
732 canvas_p0_.y + y + (grid_step / 2) - tile_id_offset),
733 kWhiteColor, label.data());
734 }
735 }
736 }
737 }
738}
739
741 const ImVec2 origin(canvas_p0_.x + scrolling_.x,
742 canvas_p0_.y + scrolling_.y); // Lock scrolled origin
743 for (int n = 0; n < points_.Size; n += 2) {
744 draw_list_->AddRect(
745 ImVec2(origin.x + points_[n].x, origin.y + points_[n].y),
746 ImVec2(origin.x + points_[n + 1].x, origin.y + points_[n + 1].y),
747 kWhiteColor, 1.0f);
748 }
749
750 if (!selected_points_.empty()) {
751 for (int n = 0; n < selected_points_.size(); n += 2) {
752 draw_list_->AddRect(ImVec2(origin.x + selected_points_[n].x,
753 origin.y + selected_points_[n].y),
754 ImVec2(origin.x + selected_points_[n + 1].x + 0x10,
755 origin.y + selected_points_[n + 1].y + 0x10),
756 kWhiteColor, 1.0f);
757 }
758 }
759
760 draw_list_->PopClipRect();
761}
762
764 // Based on ImGui demo, should be adapted to use for OAM
765 ImDrawList *draw_list = ImGui::GetWindowDrawList();
766 {
767 Text("Blue shape is drawn first: appears in back");
768 Text("Red shape is drawn after: appears in front");
769 ImVec2 p0 = ImGui::GetCursorScreenPos();
770 draw_list->AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50),
771 IM_COL32(0, 0, 255, 255)); // Blue
772 draw_list->AddRectFilled(ImVec2(p0.x + 25, p0.y + 25),
773 ImVec2(p0.x + 75, p0.y + 75),
774 IM_COL32(255, 0, 0, 255)); // Red
775 ImGui::Dummy(ImVec2(75, 75));
776 }
777 ImGui::Separator();
778 {
779 Text("Blue shape is drawn first, into channel 1: appears in front");
780 Text("Red shape is drawn after, into channel 0: appears in back");
781 ImVec2 p1 = ImGui::GetCursorScreenPos();
782
783 // Create 2 channels and draw a Blue shape THEN a Red shape.
784 // You can create any number of channels. Tables API use 1 channel per
785 // column in order to better batch draw calls.
786 draw_list->ChannelsSplit(2);
787 draw_list->ChannelsSetCurrent(1);
788 draw_list->AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50),
789 IM_COL32(0, 0, 255, 255)); // Blue
790 draw_list->ChannelsSetCurrent(0);
791 draw_list->AddRectFilled(ImVec2(p1.x + 25, p1.y + 25),
792 ImVec2(p1.x + 75, p1.y + 75),
793 IM_COL32(255, 0, 0, 255)); // Red
794
795 // Flatten/reorder channels. Red shape is in channel 0 and it appears
796 // below the Blue shape in channel 1. This works by copying draw indices
797 // only (vertices are not copied).
798 draw_list->ChannelsMerge();
799 ImGui::Dummy(ImVec2(75, 75));
800 Text("After reordering, contents of channel 0 appears below channel 1.");
801 }
802}
803
804void GraphicsBinCanvasPipeline(int width, int height, int tile_size,
805 int num_sheets_to_load, int canvas_id,
806 bool is_loaded, gfx::BitmapTable &graphics_bin) {
807 gui::Canvas canvas;
808 if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)canvas_id);
809 ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
810 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
811 canvas.DrawBackground(ImVec2(width + 1, num_sheets_to_load * height + 1));
812 canvas.DrawContextMenu();
813 if (is_loaded) {
814 for (const auto &[key, value] : graphics_bin) {
815 int offset = height * (key + 1);
816 int top_left_y = canvas.zero_point().y + 2;
817 if (key >= 1) {
818 top_left_y = canvas.zero_point().y + height * key;
819 }
820 canvas.draw_list()->AddImage(
821 (void *)value.texture(),
822 ImVec2(canvas.zero_point().x + 2, top_left_y),
823 ImVec2(canvas.zero_point().x + 0x100,
824 canvas.zero_point().y + offset));
825 }
826 }
827 canvas.DrawTileSelector(tile_size);
828 canvas.DrawGrid(tile_size);
829 canvas.DrawOverlay();
830 }
831 ImGui::EndChild();
832}
833
834void BitmapCanvasPipeline(gui::Canvas &canvas, const gfx::Bitmap &bitmap,
835 int width, int height, int tile_size, bool is_loaded,
836 bool scrollbar, int canvas_id) {
837 auto draw_canvas = [](gui::Canvas &canvas, const gfx::Bitmap &bitmap,
838 int width, int height, int tile_size, bool is_loaded) {
839 canvas.DrawBackground(ImVec2(width + 1, height + 1));
840 canvas.DrawContextMenu();
841 canvas.DrawBitmap(bitmap, 2, is_loaded);
842 canvas.DrawTileSelector(tile_size);
843 canvas.DrawGrid(tile_size);
844 canvas.DrawOverlay();
845 };
846
847 if (scrollbar) {
848 if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)canvas_id);
849 ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
850 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
851 draw_canvas(canvas, bitmap, width, height, tile_size, is_loaded);
852 }
853 ImGui::EndChild();
854 } else {
855 draw_canvas(canvas, bitmap, width, height, tile_size, is_loaded);
856 }
857}
858
859} // namespace gui
860} // namespace app
861} // namespace yaze
The Renderer class represents the renderer for the Yaze application.
Definition renderer.h:28
static Renderer & GetInstance()
Definition renderer.h:30
void UpdateBitmap(gfx::Bitmap *bitmap, bool use_sdl_update=false)
Used to update a bitmap on the screen.
Definition renderer.h:63
Represents a bitmap image.
Definition bitmap.h:70
void Reformat(int format)
Definition bitmap.cc:268
absl::Status ApplyPaletteWithTransparent(const SnesPalette &palette, size_t index, int length=7)
Definition bitmap.cc:399
auto surface() const
Definition bitmap.h:186
auto texture() const
Definition bitmap.h:193
int height() const
Definition bitmap.h:180
int width() const
Definition bitmap.h:179
auto is_active() const
Definition bitmap.h:195
void WriteColor(int position, const ImVec4 &color)
Definition bitmap.cc:502
Represents a canvas for drawing and manipulating graphics.
Definition canvas.h:36
std::string canvas_id_
Definition canvas.h:211
bool DrawTileSelector(int size)
Definition canvas.cc:343
void DrawOutline(int x, int y, int w, int h)
Definition canvas.cc:514
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0), bool drag=false)
Definition canvas.cc:69
void DrawCustomHighlight(float grid_step)
Definition canvas.cc:676
void DrawRect(int x, int y, int w, int h, ImVec4 color)
Definition canvas.cc:605
void UpdateInfoGrid(ImVec2 bg_size, int tile_size, float scale=1.0f, float grid_size=64.0f, int label_id=0)
Definition canvas.cc:61
ImVec2 selected_tile_pos_
Definition canvas.h:223
void DrawInfoGrid(float grid_step=64.0f, int tile_id_offset=8, int label_id=0)
Definition canvas.cc:642
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
Definition canvas.cc:689
void DrawBitmapGroup(std::vector< int > &group, std::vector< gfx::Bitmap > &tile16_individual_, int tile_size, float scale=1.0f)
Definition canvas.cc:539
auto draw_list() const
Definition canvas.h:146
void DrawText(std::string text, int x, int y)
Definition canvas.cc:618
bool DrawSolidTilePainter(const ImVec4 &color, int size)
Definition canvas.cc:263
void DrawTileOnBitmap(int tile_size, gfx::Bitmap *bitmap, ImVec4 color)
Definition canvas.cc:322
std::string context_id_
Definition canvas.h:212
void DrawBitmap(const Bitmap &bitmap, int border_offset=0, bool ready=true)
Definition canvas.cc:464
uint64_t edit_palette_sub_index_
Definition canvas.h:208
void DrawSelectRect(int current_map, int tile_size=0x10, float scale=1.0f)
Definition canvas.cc:376
auto zero_point() const
Definition canvas.h:147
void DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color)
Definition canvas.cc:522
uint16_t edit_palette_index_
Definition canvas.h:206
std::vector< ImVec2 > selected_tiles_
Definition canvas.h:225
void DrawBitmapTable(const BitmapTable &gfx_bin)
Definition canvas.cc:501
ImVector< ImVec2 > points_
Definition canvas.h:215
bool DrawTilePainter(const Bitmap &bitmap, int size, float scale=1.0f)
Definition canvas.cc:209
void UpdateColorPainter(gfx::Bitmap &bitmap, const ImVec4 &color, const std::function< void()> &event, int tile_size, float scale=1.0f)
Definition canvas.cc:47
uint64_t edit_palette_group_name_index_
Definition canvas.h:207
ImVec2 mouse_pos_in_canvas_
Definition canvas.h:222
auto canvas_size() const
Definition canvas.h:150
void DrawContextMenu(gfx::Bitmap *bitmap=nullptr)
Definition canvas.cc:104
ImDrawList * draw_list_
Definition canvas.h:214
ImVector< ImVec2 > selected_points_
Definition canvas.h:224
ImVector< ImVector< std::string > > labels_
Definition canvas.h:216
void DrawGridLines(float grid_step)
Definition canvas.cc:627
std::unordered_map< int, gfx::Bitmap > BitmapTable
Definition bitmap.h:221
constexpr const char * kPaletteGroupAddressesKeys[]
ImVec2 AlignPosToGrid(ImVec2 pos, float scale)
Definition canvas.cc:370
void SelectablePalettePipeline(uint64_t &palette_id, bool &refresh_graphics, gfx::SnesPalette &palette)
Definition color.cc:129
constexpr uint32_t kRectangleColor
Definition canvas.cc:40
constexpr uint32_t kWhiteColor
Definition canvas.cc:41
constexpr ImGuiButtonFlags kMouseFlags
Definition canvas.cc:44
void TextWithSeparators(const absl::string_view &text)
Definition style.cc:416
void BitmapCanvasPipeline(gui::Canvas &canvas, const gfx::Bitmap &bitmap, int width, int height, int tile_size, bool is_loaded, bool scrollbar, int canvas_id)
Definition canvas.cc:834
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:162
constexpr uint32_t kOutlineRect
Definition canvas.cc:42
constexpr uint32_t kBlackColor
Definition canvas.cc:39
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:804
Definition common.cc:21