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