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